รายการ :: empty () พฤติกรรมแบบมัลติเธรด?


9

ฉันมีรายการที่ฉันต้องการให้เธรดต่าง ๆ หยิบองค์ประกอบ เพื่อหลีกเลี่ยงการล็อก mutex ที่ป้องกันรายการเมื่อว่างฉันempty()จะตรวจสอบก่อนล็อก

ไม่เป็นไรหากการโทรไปlist::empty()ไม่ถูกต้อง 100% ของเวลา ฉันเพียงต้องการหลีกเลี่ยงการหยุดทำงานหรือรบกวนการทำงานพร้อมกันlist::push()และการlist::pop()โทร

ฉันปลอดภัยempty()ไหมที่จะสมมติว่า VC ++ และ Gnu GCC บางครั้งจะผิดพลาดและไม่มีอะไรเลวร้ายไปกว่านี้

if(list.empty() == false){ // unprotected by mutex, okay if incorrect sometimes
    mutex.lock();
    if(list.empty() == false){ // check again while locked to be certain
         element = list.back();
         list.pop_back();
    }
    mutex.unlock();
}

1
ไม่คุณไม่สามารถสรุปได้ คุณสามารถใช้คอนเทนเนอร์พร้อมกันเช่น VC ของconcurrent_queue
Panagiotis Kanavos

2
@Freeree นี่ควรเป็นคำตอบ ฉันจะเพิ่มที่std::list::sizeรับประกันความซับซ้อนของเวลาคงที่ซึ่งโดยทั่วไปหมายความว่าขนาด (จำนวนโหนด) จะต้องเก็บไว้ในตัวแปรแยกต่างหาก size_ขอเรียกว่า std::list::emptyจากนั้นมีแนวโน้มที่จะส่งคืนสิ่งที่เป็นsize_ == 0และการอ่านและเขียนพร้อมกันsize_จะทำให้เกิดการแข่งขันข้อมูลดังนั้น UB
Daniel Langr

@DanielLangr "เวลาคงที่" วัดอย่างไร มันเป็นการเรียกฟังก์ชั่นเดียวหรือโปรแกรมที่สมบูรณ์?
curiousguy

1
@curtguy: DanielLangr ตอบคำถามของคุณโดย "เป็นอิสระจากจำนวนโหนดรายการ" นั่นคือคำจำกัดความที่แน่นอนของ O (1) ซึ่งหมายความว่าทุกการเรียกใช้จะดำเนินการในเวลาน้อยกว่าค่าคงที่โดยไม่คำนึงถึงองค์ประกอบ en.wikipedia.org/wiki/Big_O_notation#Orders_of_common_functionsตัวเลือกอื่น ๆ (จนกระทั่ง C ++ 11) จะเป็นเส้นตรง = O (n) ความหมายขนาดนั้นจะต้องนับองค์ประกอบ (รายการที่เชื่อมโยง) ซึ่งจะยิ่งแย่ลงสำหรับ การเกิดพร้อมกัน (การแข่งขันข้อมูลที่ชัดเจนกว่าการอ่าน / เขียนที่ไม่ใช่อะตอม)
firda

1
@currguy: การยกตัวอย่างของคุณเองด้วย dV ความซับซ้อนของเวลาคือคณิตศาสตร์ที่ จำกัด - ทุกสิ่งเหล่านี้มีการกำหนดซ้ำหรือในรูปแบบของ "มี C เช่นนั้นที่ f (N) <C สำหรับทุก N" - นั่นคือคำจำกัดความของ O (1) (สำหรับ / HW ทุกที่มีค่าคงที่เช่น C ว่าอัลโกจะจบลงด้วยเวลาน้อยกว่า C-time สำหรับอินพุตใด ๆ ) ค่าเฉลี่ยตัดจำหน่ายหมายถึงค่าเฉลี่ยซึ่งหมายความว่าอินพุตบางส่วนอาจใช้เวลานานในการประมวลผล (เช่นต้องการแฮช / จัดสรรซ้ำอีกครั้ง) แต่ก็ยังคงค่าเฉลี่ยโดยเฉลี่ย (สมมติว่าอินพุตทั้งหมดเป็นไปได้)
firda

คำตอบ:


10

ไม่เป็นไรหากการโทรไปlist::empty()ไม่ถูกต้อง 100% ของเวลา

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

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


มุมมองส่วนบุคคลโทรlist::empty()คือการกระทำที่อ่านที่มีอะไรจะทำอย่างไรกับrace-condition
NgọcKhánhNguyễn

3
@ NgọcKhánhNguyễnหากพวกเขากำลังเพิ่มองค์ประกอบเข้าไปในรายการแล้วแน่นอนที่สุดทำให้เกิดการแข่งขันข้อมูลในขณะที่คุณกำลังเขียนและอ่านขนาดในเวลาเดียวกัน
NathanOliver

6
@ NgọcKhánhNguyễnนั่นเป็นเท็จ สภาพการแข่งขันที่เป็นหรือread-write write-writeถ้าคุณไม่เชื่อฉันให้ส่วนมาตรฐานในการแข่งขันข้อมูลอ่าน
NathanOliver

1
@ NgọcKhánhNguyễn: เพราะทั้งการเขียนและการอ่านไม่ได้รับประกันว่าจะเป็นแบบปรมาณูดังนั้นจึงสามารถดำเนินการได้พร้อมกันดังนั้นการอ่านจึงสามารถเกิดความผิดพลาดได้อย่างสมบูรณ์ (เรียกว่าอ่านฉีกขาด) ลองนึกภาพการเขียนเปลี่ยน 0x00FF เป็น 0x0100 ใน MCU 8 บิตแบบ endian เล็ก ๆ น้อย ๆ เริ่มต้นด้วยการเขียนต่ำ 0xFF เป็น 0x00 และการอ่านในขณะนี้ได้รับเป็นศูนย์ว่าอ่านไบต์ทั้งสอง (เขียนเธรดช้าหรือหยุดชั่วคราว) เขียนต่อไป แต่เธรดการอ่านมีค่าผิดพลาด (ไม่ใช่ 0x00FF หรือ 0x0100 แต่ไม่คาดคิด 0x0000)
firda

1
@ NgọcKhánhNguyễnมันอาจจะอยู่ในสถาปัตยกรรมบางอย่าง แต่เครื่องเสมือน C ++ ไม่รับประกันเช่นนั้น แม้ว่าฮาร์ดแวร์ของคุณจะเป็นสิ่งที่ถูกกฎหมายสำหรับคอมไพเลอร์ในการปรับรหัสในวิธีที่คุณจะไม่เห็นการเปลี่ยนแปลงตั้งแต่นอกจากจะมีการซิงโครไนซ์เธรดแล้วก็สามารถสันนิษฐานได้ว่ามันรันเพียงเธรดเดียวและปรับให้เหมาะสม
NathanOliver

6

มีการอ่านและการเขียนเป็น (ส่วนใหญ่อาจจะไปยังsizeสมาชิกของstd::listถ้าเราคิดว่ามันเป็นชื่อที่ต้องการ) ที่ไม่ตรงกันใน reagard กับแต่ละอื่น ๆ ลองนึกภาพว่าสายด้ายหนึ่งempty()(ในด้านนอกของคุณif()) ในขณะที่หัวข้ออื่น ๆ เข้ามาด้านในและดำเนินการif() pop_back()จากนั้นคุณกำลังอ่านตัวแปรที่อาจถูกปรับเปลี่ยน นี่คือพฤติกรรมที่ไม่ได้กำหนด


2

ตัวอย่างของสิ่งต่าง ๆ ที่อาจผิดพลาดได้:

เรียบเรียงสมาร์ทพอที่จะได้เห็นว่าmutex.lock()ไม่อาจเปลี่ยนlist.empty()ค่าตอบแทนและทำให้ข้ามภายในifการตรวจสอบอย่างสมบูรณ์ในที่สุดก็นำไปสู่การอยู่ในรายชื่อที่มีองค์ประกอบสุดท้ายของมันถูกลบออกหลังจากที่ครั้งแรกpop_backif

ทำไมถึงทำอย่างนั้น? ไม่มีการซิงโครไนซ์list.empty()ดังนั้นหากมีการเปลี่ยนแปลงพร้อมกันซึ่งจะเป็นการแข่งขันของข้อมูล มาตรฐานบอกว่าโปรแกรมจะไม่มีการแข่งขันของข้อมูลดังนั้นคอมไพเลอร์จะใช้สิ่งนั้นเพื่อให้ได้รับสิทธิ์ (ไม่เช่นนั้นจะไม่สามารถทำการปรับให้เหมาะสมได้เลย) ดังนั้นจึงสามารถสมมติมุมมองแบบเธรดเดียวบน unsynchronized list.empty()และสรุปว่ามันจะต้องคงที่

นี่เป็นเพียงหนึ่งในการปรับแต่งหลายอย่าง (หรือพฤติกรรมของฮาร์ดแวร์) ที่อาจทำลายรหัสของคุณ


คอมไพเลอร์ที่ปัจจุบันไม่ได้ดูเหมือนจะต้องการที่จะเพิ่มประสิทธิภาพa.load()+a.load()...
curiousguy

1
@currguy วิธีที่ต้องการเพิ่มประสิทธิภาพที่? คุณขอความสอดคล้องตามลำดับเต็มรูปแบบที่นั่นดังนั้นคุณจะได้รับ ...
Max Langhof

@ MaxLanghof คุณไม่คิดว่าการเพิ่มประสิทธิภาพจะa.load()*2ชัดเจนหรือ? แม้จะไม่ได้a.load(rel)+b.load(rel)-a.load(rel)รับการปรับแต่งแล้ว ไม่มีอะไรที่เป็น. ทำไมคุณถึงคาดว่าล็อค (ซึ่งโดยส่วนใหญ่มีความสอดคล้อง seq) เพื่อเพิ่มประสิทธิภาพมากขึ้น?
curiousguy

@crossguy เพราะการสั่งซื้อหน่วยความจำของการเข้าถึงที่ไม่ใช่อะตอม (ที่นี่ก่อนและหลังการล็อค) และ atomics แตกต่างกันอย่างสิ้นเชิง? ฉันไม่คาดหวังว่าการล็อกจะได้รับการปรับให้เหมาะสมที่สุด "มากกว่า" ฉันคาดว่าการเข้าถึงที่ไม่ซิงโครไนซ์จะได้รับการปรับปรุงให้ดีที่สุดมากกว่าการเข้าถึงที่สอดคล้องกันตามลำดับ การปรากฏตัวของล็อคไม่เกี่ยวข้องกับจุดของฉัน และไม่มีการคอมไพเลอร์ไม่ได้รับอนุญาตในการเพิ่มประสิทธิภาพในการa.load() + a.load() 2 * a.load()อย่าลังเลที่จะถามคำถามเกี่ยวกับเรื่องนี้หากคุณต้องการทราบข้อมูลเพิ่มเติม
Max Langhof

@ MaxLanghof ฉันไม่รู้ว่าคุณกำลังพูดอะไรอยู่ ล็อคมีความสอดคล้องตามลำดับเป็นหลัก เหตุใดการนำไปใช้จะพยายามเพิ่มประสิทธิภาพให้กับเธรดดั้งเดิม (ล็อค) และไม่ใช่บางส่วน (อะตอมมิก)? คุณคาดหวังว่าการเข้าถึงแบบไม่ใช้อะตอมมิกส์จะได้รับการปรับให้เหมาะสมกับการใช้งานของอะตอมมิกส์หรือไม่?
curiousguy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.