ฉันคิดว่าทั้งคู่กำลังทำงานเดียวกันคุณตัดสินใจเลือกที่จะใช้ในการซิงโครไนซ์อย่างไร
ฉันคิดว่าทั้งคู่กำลังทำงานเดียวกันคุณตัดสินใจเลือกที่จะใช้ในการซิงโครไนซ์อย่างไร
คำตอบ:
ทฤษฎี
ในทางทฤษฎีเมื่อเธรดพยายามล็อก mutex และไม่สำเร็จเนื่องจาก mutex ถูกล็อกอยู่แล้วเธรดจะเข้าสู่โหมดสลีปทันทีจึงอนุญาตให้เธรดอื่นรัน มันจะยังคงนอนต่อไปจนกว่าจะถูกปลุกขึ้นมาซึ่งจะเป็นกรณีที่การปลดล็อก mutex ด้วยสิ่งใดก็ตามที่เธรดล็อคไว้ก่อนหน้า เมื่อเธรดพยายามล็อก spinlock และไม่สำเร็จจะพยายามล็อกใหม่อย่างต่อเนื่องจนกว่าจะสำเร็จในที่สุด ดังนั้นมันจะไม่อนุญาตให้เธรดอื่นเข้าแทนที่ (อย่างไรก็ตามระบบปฏิบัติการจะสลับไปยังเธรดอื่นอย่างแรงเมื่อซีพียูรันไทม์ของควอนตัมของเธรดปัจจุบันได้รับเกินแน่นอน)
ปัญหา
ปัญหาเกี่ยวกับ mutexes คือการทำให้เธรดเข้าสู่โหมดสลีปและปลุกพวกเขาอีกครั้งเป็นการดำเนินการที่ค่อนข้างแพงพวกเขาต้องการคำสั่ง CPU ค่อนข้างมากและใช้เวลาพอสมควร หากตอนนี้ mutex ถูกล็อคไว้เพียงระยะเวลาสั้น ๆ เวลาที่ใช้ในการทำให้เธรดเข้าสู่โหมดสลีปและปลุกอีกครั้งอาจเกินเวลาที่เธรดนอนหลับจริงและอาจเกินเวลาที่เธรดจะ ได้สูญเปล่าโดยการสำรวจอย่างต่อเนื่องใน spinlock ในทางกลับกันการสำรวจ spinlock จะทำให้เสียเวลาของ CPU อย่างต่อเนื่องและหากล็อคไว้นานกว่านี้จะทำให้เสียเวลา CPU มากขึ้นและจะดีขึ้นมากถ้าเธรดนอนหลับแทน
การแก้ไขปัญหา
การใช้ spinlocks บนระบบ single-core / single-CPU นั้นไม่เหมาะสมเนื่องจากตราบใดที่การล็อกแบบ spinlock กำลังปิดกั้น CPU core ที่มีอยู่เท่านั้นไม่มีเธรดอื่นสามารถทำงานได้และเนื่องจากไม่มีเธรดอื่นสามารถทำงานได้ล็อคจะไม่ทำงาน ถูกปลดล็อคอย่างใดอย่างหนึ่ง IOW, spinlock ทำให้สิ้นเปลืองเวลาของ CPU ในระบบเหล่านั้นเท่านั้น หากเธรดถูกพักการทำงานแทนเธรดอื่นสามารถรันได้ในครั้งเดียวอาจปลดล็อกและจากนั้นอนุญาตให้เธรดแรกทำการประมวลผลต่อเมื่อมันตื่นขึ้นมาอีกครั้ง
บนระบบ multi-core / multi-CPU ที่มีการล็อกจำนวนมากที่เก็บไว้ในระยะเวลาอันสั้นเท่านั้นเวลาที่เสียไปสำหรับการทำให้เธรดเข้าสู่โหมดสลีปและการปลุกอีกครั้งอาจลดประสิทธิภาพของรันไทม์อย่างเห็นได้ชัด เมื่อใช้ Spinlocks แทนเธรดจะได้รับโอกาสในการใช้ประโยชน์จากควอนตัมเต็มรูปแบบของพวกเขา (ปิดกั้นในช่วงเวลาสั้น ๆ เท่านั้น แต่จากนั้นทำงานต่อไปทันที) นำไปสู่การประมวลผลที่สูงขึ้นมาก
การปฏิบัติ
เนื่องจากโปรแกรมเมอร์ส่วนใหญ่ไม่สามารถรู้ล่วงหน้าได้ถ้า mutex หรือ spinlocks จะดีกว่า (เช่นเนื่องจากจำนวนคอร์ CPU ของสถาปัตยกรรมเป้าหมายไม่เป็นที่รู้จัก) และระบบปฏิบัติการไม่สามารถรู้ได้ว่ารหัสบางส่วนได้รับการปรับให้เหมาะสมกับแกนเดี่ยวหรือ สภาพแวดล้อมแบบมัลติคอร์ระบบส่วนใหญ่ไม่แยกความแตกต่างระหว่าง mutexes และ spinlock อย่างเคร่งครัด ในความเป็นจริงระบบปฏิบัติการที่ทันสมัยส่วนใหญ่มีลูกผสม mutex และลูกผสม spinlock สิ่งนี้หมายความว่าอย่างไร
ไฮบริด mutex ทำงานเหมือน spinlock ในตอนแรกในระบบมัลติคอร์ หากเธรดไม่สามารถล็อก mutex มันจะไม่ถูกพักในทันทีเนื่องจาก mutex อาจถูกปลดล็อกในไม่ช้าดังนั้น mutex จะทำงานก่อนเหมือนกับ spinlock เฉพาะในกรณีที่ยังไม่ได้รับการล็อคหลังจากระยะเวลาหนึ่ง (หรือลองใหม่หรือปัจจัยการวัดอื่น ๆ ) เธรดจะเข้าสู่โหมดสลีปจริง ๆ หากโค้ดเดียวกันรันบนระบบที่มีแกนประมวลเดียวเท่านั้น mutex จะไม่ทำการหมุน แต่อย่างที่เห็นด้านบนซึ่งไม่เป็นประโยชน์
สปินล็อคไฮบริดจะทำงานเหมือนสปิลล็อคทั่วไปในตอนแรก แต่เพื่อหลีกเลี่ยงการเสียเวลาของ CPU มากเกินไปอาจมีกลยุทธ์การถอยกลับ โดยทั่วไปจะไม่ทำให้เธรดเข้าสู่โหมดสลีป (เนื่องจากคุณไม่ต้องการให้เกิดขึ้นเมื่อใช้ spinlock) แต่อาจตัดสินใจหยุดเธรด (ทันทีหรือหลังจากระยะเวลาหนึ่ง) และอนุญาตให้เธรดอื่นรัน ซึ่งเป็นการเพิ่มโอกาสในการปลดล็อคของ spinlock (โดยปกติแล้วเธรดสวิทช์จะมีราคาที่ถูกกว่าการใช้ด้ายที่จะเข้าสู่โหมดสลีปและปลุกอีกครั้งในภายหลังแม้ว่าจะไม่ไกล)
สรุป
หากมีข้อสงสัยให้ใช้ mutex โดยปกติแล้วพวกเขาจะเป็นตัวเลือกที่ดีกว่าและระบบที่ทันสมัยส่วนใหญ่จะอนุญาตให้พวกเขาหมุนเร็วในเวลาอันสั้นถ้ามันเป็นประโยชน์ การใช้ spinlock บางครั้งสามารถปรับปรุงประสิทธิภาพได้ แต่ภายใต้เงื่อนไขบางประการและความจริงที่ว่าคุณมีข้อสงสัยมากกว่าจะบอกฉันว่าคุณไม่ได้ทำงานในโครงการใด ๆ ที่อาจเกิดประโยชน์ได้ คุณอาจพิจารณาใช้ "ล็อควัตถุ" ของคุณเองที่สามารถใช้ spinlock หรือ mutex ภายใน (เช่นพฤติกรรมนี้สามารถกำหนดค่าได้เมื่อสร้างวัตถุดังกล่าว) เริ่มใช้ mutexes ทุกที่และถ้าคุณคิดว่าการใช้ spinlock ที่ไหนสักแห่งอาจจริง ๆ ช่วยลองดูและเปรียบเทียบผลลัพธ์ (เช่นใช้ profiler) แต่ให้แน่ใจว่าได้ทดสอบทั้งสองกรณี
จริง ๆ แล้วไม่ใช่ iOS ที่เจาะจง แต่ iOS เป็นแพลตฟอร์มที่นักพัฒนาส่วนใหญ่อาจประสบปัญหานั้น: หากระบบของคุณมีตัวกำหนดตารางเวลาเธรดที่ไม่รับประกันว่าเธรดใด ๆ ไม่ว่าจะมีความสำคัญต่ำเพียงใดก็ตาม จากนั้น spinlocks จะนำไปสู่การหยุดชะงักถาวร ตัวกำหนดตารางเวลา iOS แยกความแตกต่างของคลาสของเธรดและเธรดในคลาสที่ต่ำกว่าจะทำงานเฉพาะในกรณีที่ไม่มีเธรดในคลาสที่สูงกว่าต้องการรันเช่นกัน ไม่มีกลยุทธ์การถอยกลับสำหรับสิ่งนี้ดังนั้นหากคุณมีเธรดระดับสูงอย่างถาวรเธรดระดับต่ำจะไม่ได้รับเวลา CPU ใด ๆ ดังนั้นจึงไม่มีโอกาสที่จะทำงานใด ๆ
ปัญหาปรากฏดังต่อไปนี้: รหัสของคุณได้รับ spinlock ในเธรดระดับพรีโนต่ำและในขณะที่มันอยู่ตรงกลางของการล็อคเวลาควอนตัมเกินเวลาและเธรดหยุดทำงาน วิธีเดียวที่จะปล่อยสปินล็อคนี้ได้อีกครั้งคือถ้าเธรดระดับพรีโนต่ำนั้นได้รับเวลา CPU อีกครั้ง แต่สิ่งนี้ไม่รับประกัน คุณอาจมีเธรดระดับสูงของ prio สองสามตัวที่ต้องการเรียกใช้อย่างต่อเนื่องและตัวกำหนดตารางงานจะจัดลำดับความสำคัญเหล่านั้นเสมอ หนึ่งในนั้นอาจวิ่งข้าม spinlock และพยายามที่จะรับมันซึ่งเป็นไปไม่ได้แน่นอนและระบบจะทำให้ได้ ปัญหาคือ: เธรดที่ให้ผลใช้ได้ทันทีสำหรับการทำงานอีกครั้ง! การมี prio ที่สูงกว่าเธรดที่ล็อกล็อกเธรดที่ล็อคจะไม่มีโอกาสได้รับ CPU runtime
เหตุใดปัญหานี้จึงไม่เกิดขึ้นกับ mutexes เมื่อเธรด prio สูงไม่สามารถรับ mutex มันจะไม่ยอมให้มันอาจหมุนไปเล็กน้อย แต่ในที่สุดจะถูกส่งไปนอน เธรดที่กำลังหลับอยู่จะไม่สามารถทำงานได้จนกว่าจะถูกปลุกโดยเหตุการณ์เช่นเหตุการณ์เช่นการปลดล็อก mutex ที่กำลังรออยู่ แอปเปิ้ลได้ตระหนักถึงปัญหาที่และได้เลิกจึงOSSpinLock
เป็นผล os_unfair_lock
ล็อคใหม่ที่เรียกว่า การล็อกนี้หลีกเลี่ยงสถานการณ์ที่กล่าวถึงข้างต้นเนื่องจากทราบถึงลำดับความสำคัญของเธรดที่ต่างกัน หากคุณแน่ใจว่าการใช้ spinlock เป็นความคิดที่ดีในโครงการ iOS ของคุณให้ใช้มัน อยู่ห่างจากOSSpinLock
! และภายใต้สถานการณ์ที่ไม่มีการใช้ spinlock ของคุณเองใน iOS! หากมีข้อสงสัยให้ใช้ mutex! macOS ไม่ได้รับผลกระทบจากปัญหานี้เนื่องจากมีตัวจัดกำหนดการเธรดที่แตกต่างกันซึ่งจะไม่อนุญาตให้เธรดใด ๆ (แม้แต่เธรด prio ต่ำ) เพื่อ "รันแห้ง" ในเวลา CPU แต่สถานการณ์เดียวกันอาจเกิดขึ้นที่นั่นและจะทำให้แย่มาก ประสิทธิภาพจึงOSSpinLock
เลิกใช้แล้วใน macOS เช่นกัน
ดำเนินการตามคำแนะนำของ Mecki บทความนี้pthread mutex vs pthread spinlockในบล็อกของ Alexander Sandler, Alex บน Linux แสดงวิธีspinlock
& mutexes
สามารถนำไปใช้เพื่อทดสอบพฤติกรรมโดยใช้ #ifdef
อย่างไรก็ตามให้แน่ใจว่าได้ทำการโทรครั้งสุดท้ายตามการสังเกตของคุณการทำความเข้าใจตามตัวอย่างที่ให้มาเป็นกรณีแยกความต้องการโครงการสภาพแวดล้อมของคุณอาจแตกต่างกันโดยสิ้นเชิง
โปรดทราบว่าในบางสภาพแวดล้อมและเงื่อนไข (เช่นทำงานบน windows ในระดับการจัดส่ง> = DISPATCH LEVEL) คุณไม่สามารถใช้ mutex ได้ แต่จะเป็น spinlock ในยูนิกซ์ - สิ่งเดียวกัน
นี่คือคำถามที่เทียบเท่ากับคู่แข่งของเว็บไซต์ยูนิกซ์ stackexchange: /unix/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something- มากกว่า
ข้อมูลเกี่ยวกับการจัดส่งบนระบบ windows: http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc
คำตอบของ Mecki ค่อนข้างตอกย้ำมัน อย่างไรก็ตามในตัวประมวลผลเดียวการใช้ spinlock อาจสมเหตุสมผลเมื่องานกำลังรอการล็อคที่จะได้รับจาก Interrupt Service Routine การขัดจังหวะจะถ่ายโอนการควบคุมไปยัง ISR ซึ่งจะพร้อมทรัพยากรสำหรับใช้งานโดยรองาน มันจะจบลงด้วยการปลดล็อคก่อนที่จะให้การควบคุมกลับไปที่งานที่ถูกขัดจังหวะ ภารกิจการหมุนจะพบว่ามี spinlock และดำเนินการต่อ
กลไกการซิงโครไนซ์ Spinlock และ Mutex นั้นเป็นเรื่องธรรมดาในปัจจุบัน
ลองคิดเกี่ยวกับ Spinlock ก่อน
โดยทั่วไปจะเป็นการรอที่ไม่ว่างซึ่งหมายความว่าเราต้องรอการล็อกที่ระบุก่อนที่เราจะสามารถดำเนินการต่อไปได้ แนวคิดง่ายมากในขณะที่การใช้มันไม่ได้อยู่ในกรณี ตัวอย่างเช่น: ถ้าล็อคไม่ได้ถูกปล่อยออกมาเธรดนั้นสลับออกและเข้าสู่สถานะสลีปเราควรจัดการกับมันอย่างไร วิธีจัดการกับการล็อคการซิงโครไนซ์เมื่อสองเธรดร้องขอการเข้าถึงพร้อมกัน?
โดยทั่วไปความคิดที่ใช้งานง่ายที่สุดคือการจัดการกับการซิงโครไนซ์ผ่านตัวแปรเพื่อปกป้องส่วนที่สำคัญ แนวคิดของ Mutex คล้ายกัน แต่ก็ยังคงแตกต่างกัน มุ่งเน้น: การใช้งาน CPU Spinlock ใช้เวลา CPU เพื่อรอดำเนินการดังนั้นเราจึงสามารถสรุปความแตกต่างระหว่างสองสิ่งนี้:
ในสภาพแวดล้อมแบบมัลติคอร์ที่เป็นเนื้อเดียวกันหากเวลาที่ใช้ในส่วนที่สำคัญน้อยกว่าการใช้ Spinlock เพราะเราสามารถลดเวลาสลับบริบทได้ (การเปรียบเทียบแบบ Single-core ไม่สำคัญเพราะบางระบบใช้งาน Spinlock ที่อยู่ตรงกลางของสวิตช์)
ใน Windows การใช้ Spinlock จะอัปเกรดเธรดเป็น DISPATCH_LEVEL ซึ่งในบางกรณีอาจไม่ได้รับอนุญาตดังนั้นคราวนี้เราต้องใช้ Mutex (APC_LEVEL)
การใช้ spinlocks บนระบบ single-core / single-CPU นั้นไม่เหมาะสมเนื่องจากตราบใดที่การล็อกแบบ spinlock กำลังปิดกั้น CPU core ที่มีอยู่เท่านั้นไม่มีเธรดอื่นที่สามารถทำงานได้และเนื่องจากไม่มีเธรดอื่นสามารถทำงานได้ ถูกปลดล็อคอย่างใดอย่างหนึ่ง IOW, spinlock ทำให้สิ้นเปลืองเวลาของ CPU ในระบบเหล่านั้นเท่านั้น
นี่เป็นสิ่งที่ผิด ไม่มีการสูญเสียของ cpu cycles ในการใช้ spinlock บนระบบประมวลผล uni เพราะเมื่อกระบวนการหนึ่งใช้ spin lock การจองถูกปิดใช้งานดังนั้นจะไม่มีใครหมุนอีกเลย! มันเป็นเพียงแค่การใช้มันที่ไม่สมเหตุสมผลเลย! ดังนั้น spinlocks ในระบบ Uni จะถูกแทนที่ด้วย preempt_disable ในเวลารวบรวมโดยเคอร์เนล!