std :: lock_guard หรือ std :: scoped_lock?


157

C ++ 17 แนะนำคลาสล็อคใหม่ที่เรียกว่าstd::scoped_lock.

เมื่อพิจารณาจากเอกสารแล้วจะดูเหมือนกับstd::lock_guardคลาสที่มีอยู่แล้ว

อะไรคือความแตกต่างและฉันควรใช้เมื่อใด?

คำตอบ:


134

scoped_lockเป็นรุ่นที่เหนือกว่าอย่างเคร่งครัดของlock_guardที่ล็อคจำนวนข้อของ mutexes ทั้งหมดในครั้งเดียว (โดยใช้ขั้นตอนวิธีการหยุดชะงักหลีกเลี่ยงเช่นเดียวstd::lock) ในรหัสใหม่คุณควรใช้scoped_lockเท่านั้น

เหตุผลเดียวที่lock_guardยังคงมีอยู่คือเพื่อความเข้ากันได้ ไม่สามารถลบได้เพียงเพราะใช้ในรหัสปัจจุบัน ยิ่งไปกว่านั้นมันพิสูจน์แล้วว่าไม่พึงปรารถนาที่จะเปลี่ยนคำจำกัดความ (จากยูนารีเป็นตัวแปร) เพราะนั่นเป็นสิ่งที่สังเกตได้เช่นกันและด้วยเหตุนี้จึงทำลายการเปลี่ยนแปลง (แต่ด้วยเหตุผลทางเทคนิคเล็กน้อย)


9
นอกจากนี้ด้วยการหักอาร์กิวเมนต์แม่แบบคลาสคุณไม่จำเป็นต้องแสดงรายการประเภทที่ล็อคได้
Nicol Bolas

3
@NicolBolas: นั่นเป็นความจริง lock_guardแต่ที่ยังนำไปใช้ แต่แน่นอนว่ามันทำให้คลาสยามใช้งานง่ายขึ้นเล็กน้อย
Kerrek SB

8
scoped_lock คือ C ++ 17 เท่านั้น
Shital Shah

3
เนื่องจากเป็น c ++ 17 ความเข้ากันได้จึงเป็นเหตุผลที่ดีอย่างยิ่งสำหรับการมีอยู่ ฉันยังไม่เห็นด้วยอย่างรุนแรงกับคำกล่าวอ้างสัมบูรณ์ของ "คุณควรใช้" เมื่อหมึกยังแห้งจากมาตรฐานนี้
Paul Childs

96

ข้อแตกต่างที่สำคัญประการเดียวคือstd::scoped_lockมีตัวสร้างตัวแปรที่ใช้ mutex มากกว่าหนึ่งตัว สิ่งนี้ช่วยให้สามารถล็อค mutexes หลายตัวในทางหลีกเลี่ยงการหยุดชะงักราวกับว่าstd::lockมีการใช้

{
    // safely locked as if using std::lock
    std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);     
}

ก่อนหน้านี้คุณต้องเต้นเล็กน้อยเพื่อล็อค mutexes หลายตัวด้วยวิธีที่ปลอดภัยโดยใช้std::lockตามที่อธิบายคำตอบนี้

การเพิ่มการล็อกขอบเขตช่วยให้ใช้งานได้ง่ายขึ้นและหลีกเลี่ยงข้อผิดพลาดที่เกี่ยวข้อง คุณสามารถพิจารณาstd::lock_guardเลิกใช้งานได้ กรณีอาร์กิวเมนต์เดียวของstd::scoped_lockสามารถนำไปใช้เป็นความเชี่ยวชาญและคุณไม่ต้องกลัวเกี่ยวกับปัญหาด้านประสิทธิภาพที่อาจเกิดขึ้น

GCC 7 แล้วมีการสนับสนุนstd::scoped_lockซึ่งสามารถมองเห็นได้ที่นี่

สำหรับข้อมูลเพิ่มเติมคุณอาจต้องการอ่านกระดาษมาตรฐาน


9
ตอบคำถามของคุณเองหลังจากนั้นเพียง 10 นาที คุณไม่รู้จริงๆเหรอ?
Walter

26
@ วอลเตอร์ฉันทำstackoverflow.blog/2011/07/01/…
Stephan Dollberg

3
เมื่อนำขึ้นพิจารณาคำตอบคือ "ไม่มีอะไร" อาจเป็นไปได้ว่ากรณีที่เสื่อมถอยของอัลกอริทึมบางอย่างนี่เป็นสิ่งที่ถูกต้อง หรืออาจเป็นไปได้ว่าคนจำนวนมากไม่ได้ตั้งใจล็อกอะไรไว้เมื่อตั้งใจจะล็อกบางสิ่งเป็นปัญหาที่พบบ่อย ฉันไม่แน่ใจจริงๆ
Howard Hinnant

3
scoped_lock lk; // locks all mutexes in scope@HowardHinnant: LGTM
Kerrek SB

2
@KerrekSB: scoped_lock lk;เป็นชวเลขใหม่สำหรับscoped_lock<> lk;. มีอยู่ไม่ mutexes คุณพูดถูก ;-)
Howard Hinnant

41

คำตอบที่ล่าช้าและส่วนใหญ่ตอบสนองต่อ:

คุณสามารถพิจารณาstd::lock_guardเลิกใช้งานได้

สำหรับกรณีที่พบว่าหนึ่งในความต้องการที่จะล็อคตรงหนึ่ง mutex, std::lock_guardมี API ที่เป็นเล็ก ๆ น้อย ๆ scoped_lockปลอดภัยในการใช้กว่า

ตัวอย่างเช่น:

{
   std::scoped_lock lock;  // protect this block
   ...
}

ตัวอย่างข้อมูลข้างต้นน่าจะเป็นข้อผิดพลาดขณะทำงานโดยไม่ได้ตั้งใจเนื่องจากรวบรวมแล้วไม่ทำอะไรเลย coder อาจหมายถึง:

{
   std::scoped_lock lock{mut};  // protect this block
   ...
}

ตอนนี้mutมันล็อค / ปลดล็อค

หากถูกใช้ในสองตัวอย่างข้างต้นแทนตัวอย่างแรกเป็นข้อผิดพลาดเวลารวบรวมแทนของข้อผิดพลาดเวลาทำงานและตัวอย่างที่สองมีการทำงานเหมือนกันกับรุ่นที่ใช้lock_guardscoped_lock

ดังนั้นคำแนะนำของฉันคือใช้เครื่องมือที่ง่ายที่สุดสำหรับงาน:

  1. lock_guard หากคุณต้องการล็อค 1 mutex สำหรับขอบเขตทั้งหมด

  2. scoped_lock หากคุณต้องการล็อค mutexes จำนวนหนึ่งที่ไม่ตรงกับ 1

  3. unique_lockหากคุณต้องการปลดล็อกภายในขอบเขตของบล็อก (ซึ่งรวมถึงการใช้กับ a condition_variable)

คำแนะนำนี้ไม่ ได้หมายความว่าscoped_lockควรออกแบบใหม่เพื่อไม่ยอมรับ 0 mutexes มีกรณีการใช้งานที่ถูกต้องซึ่งเป็นที่พึงปรารถนาสำหรับscoped_lockการยอมรับชุดพารามิเตอร์เทมเพลตตัวแปรซึ่งอาจว่างเปล่า และเคสเปล่าไม่ควรล็อคอะไร.

และนั่นคือสาเหตุที่lock_guardไม่เลิกใช้งาน scoped_lock และ unique_lockอาจเป็นส่วนเหนือของฟังก์ชันการทำงานของlock_guardแต่ความจริงนั้นเป็นดาบสองคม บางครั้งมันก็สำคัญพอ ๆ กับสิ่งที่ประเภทจะไม่ทำ (โครงสร้างเริ่มต้นในกรณีนี้)


14

นี่คือตัวอย่างและคำพูดจากC ++ Concurrency in Action :

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == & rhs)
        return;
    std::lock(lhs.m, rhs.m);
    std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
    std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
    swap(lhs.some_detail, rhs.some_detail);
}

เทียบกับ

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == &rhs)
        return;
    std::scoped_lock guard(lhs.m, rhs.m);
    swap(lhs.some_detail, rhs.some_detail);
}

การมีอยู่ของstd::scoped_lockหมายความว่ากรณีส่วนใหญ่ที่คุณเคยใช้std::lockก่อน c ++ 17 ตอนนี้สามารถเขียนได้โดยstd::scoped_lockมีโอกาสเกิดความผิดพลาดน้อยลงซึ่งอาจเป็นสิ่งที่ดีเท่านั้น!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.