C ++ 17 แนะนำคลาสล็อคใหม่ที่เรียกว่าstd::scoped_lock
.
เมื่อพิจารณาจากเอกสารแล้วจะดูเหมือนกับstd::lock_guard
คลาสที่มีอยู่แล้ว
อะไรคือความแตกต่างและฉันควรใช้เมื่อใด?
C ++ 17 แนะนำคลาสล็อคใหม่ที่เรียกว่าstd::scoped_lock
.
เมื่อพิจารณาจากเอกสารแล้วจะดูเหมือนกับstd::lock_guard
คลาสที่มีอยู่แล้ว
อะไรคือความแตกต่างและฉันควรใช้เมื่อใด?
คำตอบ:
scoped_lock
เป็นรุ่นที่เหนือกว่าอย่างเคร่งครัดของlock_guard
ที่ล็อคจำนวนข้อของ mutexes ทั้งหมดในครั้งเดียว (โดยใช้ขั้นตอนวิธีการหยุดชะงักหลีกเลี่ยงเช่นเดียวstd::lock
) ในรหัสใหม่คุณควรใช้scoped_lock
เท่านั้น
เหตุผลเดียวที่lock_guard
ยังคงมีอยู่คือเพื่อความเข้ากันได้ ไม่สามารถลบได้เพียงเพราะใช้ในรหัสปัจจุบัน ยิ่งไปกว่านั้นมันพิสูจน์แล้วว่าไม่พึงปรารถนาที่จะเปลี่ยนคำจำกัดความ (จากยูนารีเป็นตัวแปร) เพราะนั่นเป็นสิ่งที่สังเกตได้เช่นกันและด้วยเหตุนี้จึงทำลายการเปลี่ยนแปลง (แต่ด้วยเหตุผลทางเทคนิคเล็กน้อย)
lock_guard
แต่ที่ยังนำไปใช้ แต่แน่นอนว่ามันทำให้คลาสยามใช้งานง่ายขึ้นเล็กน้อย
ข้อแตกต่างที่สำคัญประการเดียวคือ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
ซึ่งสามารถมองเห็นได้ที่นี่
สำหรับข้อมูลเพิ่มเติมคุณอาจต้องการอ่านกระดาษมาตรฐาน
scoped_lock lk; // locks all mutexes in scope
@HowardHinnant: LGTM
scoped_lock lk;
เป็นชวเลขใหม่สำหรับscoped_lock<> lk;
. มีอยู่ไม่ mutexes คุณพูดถูก ;-)
คำตอบที่ล่าช้าและส่วนใหญ่ตอบสนองต่อ:
คุณสามารถพิจารณา
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_guard
scoped_lock
ดังนั้นคำแนะนำของฉันคือใช้เครื่องมือที่ง่ายที่สุดสำหรับงาน:
lock_guard
หากคุณต้องการล็อค 1 mutex สำหรับขอบเขตทั้งหมด
scoped_lock
หากคุณต้องการล็อค mutexes จำนวนหนึ่งที่ไม่ตรงกับ 1
unique_lock
หากคุณต้องการปลดล็อกภายในขอบเขตของบล็อก (ซึ่งรวมถึงการใช้กับ a condition_variable
)
คำแนะนำนี้ไม่ ได้หมายความว่าscoped_lock
ควรออกแบบใหม่เพื่อไม่ยอมรับ 0 mutexes มีกรณีการใช้งานที่ถูกต้องซึ่งเป็นที่พึงปรารถนาสำหรับscoped_lock
การยอมรับชุดพารามิเตอร์เทมเพลตตัวแปรซึ่งอาจว่างเปล่า และเคสเปล่าไม่ควรล็อคอะไร.
และนั่นคือสาเหตุที่lock_guard
ไม่เลิกใช้งาน scoped_lock
และ unique_lock
อาจเป็นส่วนเหนือของฟังก์ชันการทำงานของlock_guard
แต่ความจริงนั้นเป็นดาบสองคม บางครั้งมันก็สำคัญพอ ๆ กับสิ่งที่ประเภทจะไม่ทำ (โครงสร้างเริ่มต้นในกรณีนี้)
นี่คือตัวอย่างและคำพูดจาก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
มีโอกาสเกิดความผิดพลาดน้อยลงซึ่งอาจเป็นสิ่งที่ดีเท่านั้น!