ทำไมฉันไม่สามารถตรวจสอบว่า Mutex ถูกล็อคหรือไม่


28

C ++ 14 ดูเหมือนจะไม่ได้ใช้กลไกสำหรับตรวจสอบว่ามีการstd::mutexล็อกอยู่หรือไม่ ดูคำถาม SO นี้:

https://stackoverflow.com/questions/21892934/how-to-assert-if-a-stdmutex-is-locked

มีหลายวิธีเช่นนี้โดยใช้;

std::mutex::try_lock()
std::unique_lock::owns_lock()

แต่สิ่งเหล่านี้ไม่ได้เป็นโซลูชั่นที่น่าพึงพอใจโดยเฉพาะ

try_lock()ได้รับอนุญาตให้ส่งคืนค่าลบที่เป็นเท็จและมีพฤติกรรมที่ไม่ได้กำหนดหากเธรดปัจจุบันล็อค mutex นอกจากนี้ยังมีผลข้างเคียง owns_lock()ต้องมีการก่อสร้างที่อยู่ด้านบนของเดิมunique_lockstd::mutex

เห็นได้ชัดว่าฉันสามารถม้วนตัวเอง แต่ฉันค่อนข้างเข้าใจแรงจูงใจสำหรับอินเทอร์เฟซปัจจุบัน

ความสามารถในการตรวจสอบสถานะของ mutex (เช่นstd::mutex::is_locked()) ดูเหมือนจะไม่เป็นการร้องขอที่ลึกลับสำหรับฉันดังนั้นฉันจึงสงสัยว่าคณะกรรมการมาตรฐานได้ละเว้นคุณลักษณะนี้โดยไม่ได้ตั้งใจ

ทำไม?

แก้ไข:ตกลงดังนั้นกรณีการใช้งานนี้อาจไม่เหมือนที่ฉันคาดไว้ดังนั้นฉันจะอธิบายสถานการณ์ของฉันโดยเฉพาะ ฉันมีอัลกอริทึมการเรียนรู้ของเครื่องซึ่งกระจายอยู่ในหลายเธรด แต่ละเธรดทำงานแบบอะซิงโครนัสและกลับไปยังพูลต้นแบบเมื่อปัญหาการปรับให้เหมาะสมเสร็จสมบูรณ์

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


42
คุณไม่สามารถตรวจสอบได้อย่างมีเหตุผลว่า Mutex ถูกล็อกหรือไม่เนื่องจากหนึ่งนาโนวินาทีหลังจากการตรวจสอบสามารถปลดล็อคหรือล็อคได้ ดังนั้นหากคุณเขียนว่า "if (mutex_is_locked ()) ... " จากนั้น mutex_is_locked สามารถส่งคืนผลลัพธ์ที่ถูกต้อง แต่เมื่อถึงเวลาที่ดำเนินการ "if" มันผิด
gnasher729

1
นี้ ^ คุณหวังว่าจะได้รับข้อมูลที่เป็นประโยชน์อะไรis_lockedบ้าง
ไร้ประโยชน์

3
นี่ให้ความรู้สึกเหมือนเป็นปัญหา XY เหตุใดคุณจึงพยายามป้องกันการนำพ่อแม่ไปใช้ซ้ำในขณะที่เด็กกำลังสร้าง คุณมีข้อกำหนดที่ผู้ปกครองคนใดคนหนึ่งอาจมีลูกเพียงคนเดียวหรือไม่? ล็อคของคุณจะไม่ป้องกันสิ่งนั้น คุณไม่มีรุ่นชัดเจน ถ้าไม่คุณทราบหรือไม่ว่าบุคคลที่สามารถเพิ่มประสิทธิภาพได้เร็วขึ้นจะมีความฟิตสูงกว่าเนื่องจากสามารถเลือกได้บ่อยขึ้น / เร็วขึ้น? หากคุณใช้หลายชั่วอายุทำไมคุณไม่เลือกผู้ปกครองทั้งหมดล่วงหน้าให้ปล่อยให้ผู้ปกครองดึงหัวข้อจากคิว? การสร้างลูกหลานมีราคาแพงมากจนคุณต้องมีหลายกระทู้หรือไม่?
amon

10
@quant - ฉันไม่เห็นสาเหตุที่วัตถุหลักของคุณกลายเป็น mutexes ในแอปพลิเคชันตัวอย่างของคุณจำเป็นต้องมี mutexes เลย: หากคุณมี mutex หลักที่ถูกล็อคทุกครั้งที่มีการตั้งค่าคุณสามารถใช้ตัวแปรบูลีนเพื่อระบุสถานะของพวกเขา
Periata Breatta

4
ฉันไม่เห็นด้วยกับประโยคสุดท้ายของคำถาม ค่าบูลีนที่เรียบง่ายนั้นสะอาดกว่า mutex ที่นี่ ทำให้เป็นอะตอมบูลหากคุณไม่ต้องการล็อคมาสเตอร์ mutex สำหรับ "คืนค่า" พาเรนต์
เซบาสเตียนเรดล

คำตอบ:


53

ฉันสามารถดูปัญหาที่รุนแรงอย่างน้อยสองปัญหาด้วยการดำเนินการที่แนะนำ

คนแรกที่ถูกกล่าวถึงในความคิดเห็นโดย @ gnasher729 :

คุณไม่สามารถตรวจสอบได้อย่างมีเหตุผลว่า Mutex ถูกล็อกหรือไม่เนื่องจากหนึ่งนาโนวินาทีหลังจากการตรวจสอบสามารถปลดล็อคหรือล็อคได้ ดังนั้นหากคุณเขียนif (mutex_is_locked ()) …แล้วmutex_is_lockedสามารถส่งคืนผลลัพธ์ที่ถูกต้อง แต่เมื่อถึงเวลาifดำเนินการมันผิด

วิธีเดียวที่จะตรวจสอบให้แน่ใจว่าคุณสมบัติ“ ถูกล็อคอยู่ในปัจจุบัน” ของ mutex นั้นไม่เปลี่ยนแปลงคือล็อคด้วยตัวคุณเอง

ปัญหาที่สองที่ฉันเห็นคือยกเว้นว่าคุณล็อค mutex เธรดของคุณจะไม่ซิงโครไนซ์กับเธรดที่เคยล็อค mutex ดังนั้นจึงไม่มีคำจำกัดความที่ดีที่จะพูดเกี่ยวกับ“ ก่อน” และ“ หลังจาก” และการที่ mutex ถูกล็อคหรือไม่เป็นการถามว่าแมวของSchrödigerยังมีชีวิตอยู่โดยไม่พยายามเปิดกล่องหรือไม่

หากฉันเข้าใจอย่างถูกต้องปัญหาทั้งสองจะเกิดขึ้นในกรณีของคุณโดยเฉพาะเนื่องจาก mutex หลักถูกล็อค แต่นี่ไม่ใช่กรณีทั่วไปสำหรับฉันโดยเฉพาะฉันคิดว่าคณะกรรมการทำสิ่งที่ถูกต้องโดยไม่เพิ่มฟังก์ชั่นที่อาจมีประโยชน์ในสถานการณ์พิเศษและทำให้เกิดความเสียหายต่อผู้อื่นทั้งหมด (ด้วยจิตวิญญาณของ:“ ทำให้ส่วนต่อประสานที่ใช้งานง่ายถูกต้องและใช้งานไม่ถูกต้อง”)

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


ขอบคุณนี่เป็นคำตอบที่ดี เหตุผลที่ฉันไม่ต้องการรักษาลำดับผู้ปกครองที่พร้อมอยู่ก็คือฉันต้องรักษาลำดับที่ผู้ปกครองสร้างขึ้น (เนื่องจากสิ่งนี้สั่งให้อายุการใช้งานของพวกเขา) สิ่งนี้ทำได้อย่างง่ายดายด้วยคิว LIFO ถ้าฉันเริ่มโยนสิ่งเข้าและออกฉันจะต้องรักษากลไกการสั่งซื้อแยกต่างหากซึ่งจะทำให้สิ่งต่าง ๆ ซับซ้อนดังนั้นวิธีการในปัจจุบัน
quant

14
@quant: หากคุณมีจุดประสงค์สองประการในการเข้าคิวผู้ปกครองคุณสามารถทำได้โดยใช้สองคิว ....

@quant: คุณกำลังลบรายการ (มากที่สุด) หนึ่งครั้ง แต่น่าจะทำการประมวลผลในหลาย ๆ ครั้งดังนั้นคุณจะเพิ่มประสิทธิภาพของกรณีที่หายากด้วยค่าใช้จ่ายของกรณีทั่วไป สิ่งนี้ไม่ค่อยเป็นที่พึงปรารถนา
Jerry Coffin

2
แต่มีเหตุผลที่จะถามว่าเธรดปัจจุบันล็อค mutex หรือไม่
การชดเชยที่ จำกัด

@LimitedAtonement ไม่ได้จริงๆ เมื่อต้องการทำ mutex นี้ต้องเก็บข้อมูลเพิ่มเติม (id ของเธรด) ทำให้ช้าลง Mutexes แบบเรียกซ้ำทำสิ่งนี้ไปแล้วคุณควรทำมันแทน
StaceyGirl

9

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

นั่นไม่จำเป็นอย่างสมบูรณ์ ฉันมีรายการปัญหาที่ต้องเพิ่มประสิทธิภาพรายการปัญหาที่ได้รับการปรับให้เหมาะสมในขณะนี้และรายการปัญหาที่ได้รับการปรับให้เหมาะสม (อย่าใช้ "รายการ" ตามตัวอักษรให้หมายถึง "โครงสร้างข้อมูลใด ๆ ที่เหมาะสม)

การดำเนินการเพิ่มปัญหาใหม่ไปยังรายการปัญหาที่ไม่ได้กำหนดเวลาหรือย้ายปัญหาจากรายการหนึ่งไปยังอีกรายการจะดำเนินการภายใต้การคุ้มครองของ mutex "master" เดี่ยว


1
คุณไม่คิดว่าวัตถุประเภทstd::mutexเหมาะสมกับโครงสร้างข้อมูลเช่นนี้หรือไม่?
quant

2
@quant - ไม่ std::mutexอาศัยการใช้งาน mutex ที่ระบบกำหนดไว้ซึ่งอาจใช้ทรัพยากร (เช่นที่จับ) ที่ จำกัด และช้าในการจัดสรรและ / หรือดำเนินการ การใช้ mutex เดียวเพื่อล็อคการเข้าถึงโครงสร้างข้อมูลภายในมีแนวโน้มที่จะมีประสิทธิภาพมากขึ้นและสามารถปรับขนาดได้อีกด้วย
Periata Breatta

1
พิจารณาตัวแปรเงื่อนไขด้วย พวกเขาสามารถสร้างโครงสร้างข้อมูลจำนวนมากเช่นนี้ง่ายมาก
Cort Ammon - Reinstate Monica

2

อย่างที่คนอื่น ๆ พูดกันว่าไม่มีประโยชน์กรณีใด ๆis_lockedบน mutex ซึ่งเป็นประโยชน์ใด ๆ นั่นคือสาเหตุที่ฟังก์ชั่นนี้ไม่มีอยู่

กรณีที่คุณมีปัญหาเกี่ยวกับเรื่องนี้เป็นเรื่องธรรมดาอย่างไม่น่าเชื่อมันเป็นสิ่งที่เธรดผู้ปฏิบัติงานทำซึ่งเป็นหนึ่งในนั้นถ้าไม่ใช่การใช้เธรดทั่วไป

คุณมีชั้นวางของพร้อมกล่อง 10 กล่อง คุณมี 4 คนทำงานกับกล่องเหล่านี้ คุณแน่ใจได้อย่างไรว่าคนงาน 4 คนทำงานบนกล่องที่แตกต่างกัน? คนงานคนแรกถอดกล่องออกจากชั้นวางก่อนที่จะเริ่มทำงาน ผู้ปฏิบัติงานที่สองเห็นกล่อง 9 ชั้น

ไม่มี mutexes ในการล็อกกล่องดังนั้นการเห็นสถานะของ mutex จินตภาพบนกล่องไม่จำเป็นและการใช้ mutex เป็นการเหยียดหยามเนื่องจากบูลีนผิด mutex ล็อคชั้นวาง


1

นอกเหนือจากเหตุผลสองข้อที่ให้ไว้ในคำตอบของ 5gon12eder ข้างต้นฉันต้องการเพิ่มว่าไม่จำเป็นหรือไม่ต้องการ

หากคุณถือ Mutex อยู่แล้วคุณควรจะรู้ว่าคุณกำลังถือมันอยู่! คุณไม่จำเป็นต้องถาม เช่นเดียวกับการเป็นเจ้าของบล็อกของหน่วยความจำหรือทรัพยากรอื่น ๆ คุณควรรู้ว่าคุณเป็นเจ้าของหรือไม่และเมื่อเหมาะสมที่จะปล่อย / ลบทรัพยากร
หากไม่ใช่กรณีดังกล่าวโปรแกรมของคุณได้รับการออกแบบมาไม่ดีและคุณกำลังประสบปัญหา

หากคุณต้องการเข้าถึงทรัพยากรที่ใช้ร่วมกันซึ่งได้รับการป้องกันโดย mutex และคุณยังไม่ได้ถือ mutex อยู่คุณต้องได้รับ mutex ไม่มีตัวเลือกอื่นไม่เช่นนั้นตรรกะโปรแกรมของคุณจะไม่ถูกต้อง
คุณอาจพบว่าการบล็อกที่ยอมรับได้หรือยอมรับไม่ได้ไม่ว่าในกรณีใดlock()หรือtry_lock()จะให้พฤติกรรมที่คุณต้องการ สิ่งที่คุณต้องรู้บวกและไม่ต้องสงสัยเลยว่าคุณได้รับ mutex สำเร็จหรือไม่ (ค่าส่งคืนของtry_lockบอกคุณ) มันไม่สำคัญว่าคนอื่นจะถือมันหรือว่าคุณมีความล้มเหลวลวงตา

ในกรณีอื่น ๆ ตรงไปตรงมามันไม่ใช่ธุรกิจของคุณ คุณไม่จำเป็นต้องรู้และไม่ควรรู้หรือตั้งสมมติฐาน (สำหรับปัญหาตรงเวลาและการซิงค์ที่กล่าวถึงในคำถามอื่น ๆ )


1
ถ้าฉันต้องการดำเนินการจัดอันดับบนทรัพยากรที่มีอยู่ในปัจจุบันสำหรับการล็อค
quant

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

4
@quant - "การดำเนินการจัดอันดับทรัพยากรที่มีอยู่ในปัจจุบันสำหรับล็อค" - โดยทั่วไปการทำสิ่งนี้เป็นวิธีที่ง่ายและรวดเร็วในการเขียนโค้ดที่ deadlocks ในแบบที่คุณพยายามหา การเข้าซื้อกิจการและปล่อยล็อคกำหนดคือในกรณีเกือบทุกนโยบายที่ดีที่สุด การค้นหาการล็อกตามเกณฑ์ที่สามารถเปลี่ยนแปลงได้คือการถามถึงปัญหา
Periata Breatta

@PeriataBreatta โปรแกรมของฉันไม่ได้ตั้งใจ ตอนนี้ฉันเห็นแล้วว่าคุณลักษณะนี้ไม่ธรรมดาดังนั้นฉันจึงสามารถเข้าใจการละเว้นคุณลักษณะเช่นis_locked()นั้นซึ่งอาจช่วยให้เกิดพฤติกรรมดังกล่าวได้
quant

การจัดอันดับ @quant และการล็อกเป็นปัญหาที่แยกจากกันโดยสิ้นเชิง หากคุณต้องการเรียงลำดับหรือเรียงลำดับคิวใหม่ด้วยการล็อกให้ล็อคเรียงลำดับจากนั้นปลดล็อค หากคุณต้องการis_lockedวิธีแก้ไขปัญหาของคุณจะดีกว่าที่คุณมีอยู่ในใจ
ปีเตอร์

1

คุณอาจต้องการใช้atomic_flagกับลำดับหน่วยความจำเริ่มต้น ไม่มีการแข่งขันของข้อมูลและไม่เคยมีข้อยกเว้นเช่น mutex ทำกับการปลดล็อกหลายสาย (และยกเลิกการควบคุมฉันอาจเพิ่ม ... ) อีกวิธีหนึ่งคือมีอะตอมมิก (เช่นอะตอมมิก [บูล] หรืออะตอมมิก [int] (มีวงเล็บสามเหลี่ยมไม่ใช่ [])) ซึ่งมีฟังก์ชั่นที่ดีเช่นโหลดและ compar_exchange_strong


1

ฉันต้องการเพิ่มกรณีการใช้งานสำหรับสิ่งนี้: มันจะเปิดใช้งานฟังก์ชั่นภายในเพื่อให้แน่ใจว่าเป็นเงื่อนไข / การยืนยันว่าผู้โทรถือล็อคอย่างแน่นอน

สำหรับชั้นเรียนที่มีฟังก์ชั่นภายในหลายอย่างและอาจมีฟังก์ชั่นสาธารณะจำนวนมากเรียกพวกเขามันสามารถมั่นใจได้ว่ามีคนเพิ่มฟังก์ชั่นสาธารณะอื่นที่เรียกว่าหนึ่งภายในได้รับล็อคแน่นอน

class SynchronizedClass
{

public:

void publicFunc()
{
  std::lock_guard<std::mutex>(_mutex);

  internalFuncA();
}

// A lot of code

void newPublicFunc()
{
  internalFuncA(); // whops, forgot to acquire the lock
}


private:

void internalFuncA()
{
  assert(_mutex.is_locked_by_this_thread());

  doStuffWithLockedResource();
}

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