เมื่อใดควรใช้ spinlock แทน mutex


294

ฉันคิดว่าทั้งคู่กำลังทำงานเดียวกันคุณตัดสินใจเลือกที่จะใช้ในการซิงโครไนซ์อย่างไร


1
เป็นไปได้ซ้ำของSpinlock Versus Semaphore!
Paul R

11
Mutex และ Semaphore นั้นไม่เหมือนกันดังนั้นฉันไม่คิดว่ามันจะซ้ำกัน คำตอบของบทความอ้างอิงนี้ระบุไว้อย่างถูกต้อง สำหรับรายละเอียดเพิ่มเติมดูbarrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
nanoquack

คำตอบ:


729

ทฤษฎี

ในทางทฤษฎีเมื่อเธรดพยายามล็อก 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 ที่เจาะจง แต่ 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 เช่นกัน


3
การอธิบายที่ยอดเยี่ยม ... ฉันมีข้อสงสัยเกี่ยวกับ spinlock i ฉันสามารถใช้ spinlock ใน ISR ได้หรือไม่? ถ้าไม่ใช่ทำไมไม่
haris

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

7
@ fumoboy007 "และมันจะหมุนจนกว่าจะถึงเวลาควอนตัมจะหมดอายุ" // ซึ่งหมายความว่าคุณจะเสียเวลา CPU / พลังงานแบตเตอรี่โดยไม่มีประโยชน์ใด ๆ เลยโดยไม่มีประโยชน์ใด ๆ เลย และไม่ฉันไม่มีที่ไหนเลยที่บอกว่าการแบ่งเวลาจะเกิดขึ้นในระบบแกนเดี่ยวเท่านั้นฉันกล่าวว่าในระบบคอร์เดี่ยวมีการแบ่งเวลาเท่านั้นในขณะที่มีระบบขนานขนานจริง OM multicore (และการแบ่งเวลายังไม่เกี่ยวข้องกับสิ่งที่ฉันเขียนไว้ใน ตอบ); นอกจากนี้คุณยังพลาดจุดสำคัญของ spinlock ไฮบริดและทำไมมันจึงทำงานได้ดีกับระบบเดี่ยวและมัลติคอร์
Mecki

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

2
หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับspinlocksและmutexes ที่ใช้ในเคอร์เนล Linux ฉันขอแนะนำให้อ่านบทที่ 5ของไดรเวอร์อุปกรณ์ Linuxที่ยอดเยี่ยม, Third Edition (LDD3) (mutexes: หน้า 109; Spinlocks: หน้า 116)
patryk.beza

7

ดำเนินการตามคำแนะนำของ Mecki บทความนี้pthread mutex vs pthread spinlockในบล็อกของ Alexander Sandler, Alex บน Linux แสดงวิธีspinlock& mutexesสามารถนำไปใช้เพื่อทดสอบพฤติกรรมโดยใช้ #ifdef

อย่างไรก็ตามให้แน่ใจว่าได้ทำการโทรครั้งสุดท้ายตามการสังเกตของคุณการทำความเข้าใจตามตัวอย่างที่ให้มาเป็นกรณีแยกความต้องการโครงการสภาพแวดล้อมของคุณอาจแตกต่างกันโดยสิ้นเชิง


6

โปรดทราบว่าในบางสภาพแวดล้อมและเงื่อนไข (เช่นทำงานบน 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


6

คำตอบของ Mecki ค่อนข้างตอกย้ำมัน อย่างไรก็ตามในตัวประมวลผลเดียวการใช้ spinlock อาจสมเหตุสมผลเมื่องานกำลังรอการล็อคที่จะได้รับจาก Interrupt Service Routine การขัดจังหวะจะถ่ายโอนการควบคุมไปยัง ISR ซึ่งจะพร้อมทรัพยากรสำหรับใช้งานโดยรองาน มันจะจบลงด้วยการปลดล็อคก่อนที่จะให้การควบคุมกลับไปที่งานที่ถูกขัดจังหวะ ภารกิจการหมุนจะพบว่ามี spinlock และดำเนินการต่อ


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

3

กลไกการซิงโครไนซ์ Spinlock และ Mutex นั้นเป็นเรื่องธรรมดาในปัจจุบัน

ลองคิดเกี่ยวกับ Spinlock ก่อน

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

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

ในสภาพแวดล้อมแบบมัลติคอร์ที่เป็นเนื้อเดียวกันหากเวลาที่ใช้ในส่วนที่สำคัญน้อยกว่าการใช้ Spinlock เพราะเราสามารถลดเวลาสลับบริบทได้ (การเปรียบเทียบแบบ Single-core ไม่สำคัญเพราะบางระบบใช้งาน Spinlock ที่อยู่ตรงกลางของสวิตช์)

ใน Windows การใช้ Spinlock จะอัปเกรดเธรดเป็น DISPATCH_LEVEL ซึ่งในบางกรณีอาจไม่ได้รับอนุญาตดังนั้นคราวนี้เราต้องใช้ Mutex (APC_LEVEL)


-6

การใช้ spinlocks บนระบบ single-core / single-CPU นั้นไม่เหมาะสมเนื่องจากตราบใดที่การล็อกแบบ spinlock กำลังปิดกั้น CPU core ที่มีอยู่เท่านั้นไม่มีเธรดอื่นที่สามารถทำงานได้และเนื่องจากไม่มีเธรดอื่นสามารถทำงานได้ ถูกปลดล็อคอย่างใดอย่างหนึ่ง IOW, spinlock ทำให้สิ้นเปลืองเวลาของ CPU ในระบบเหล่านั้นเท่านั้น

นี่เป็นสิ่งที่ผิด ไม่มีการสูญเสียของ cpu cycles ในการใช้ spinlock บนระบบประมวลผล uni เพราะเมื่อกระบวนการหนึ่งใช้ spin lock การจองถูกปิดใช้งานดังนั้นจะไม่มีใครหมุนอีกเลย! มันเป็นเพียงแค่การใช้มันที่ไม่สมเหตุสมผลเลย! ดังนั้น spinlocks ในระบบ Uni จะถูกแทนที่ด้วย preempt_disable ในเวลารวบรวมโดยเคอร์เนล!


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

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

ในเวลารวบรวมโดยเคอร์เนล ?
Shien

@konstantin สปินล็อคสามารถถ่ายได้ในพื้นที่เคอร์เนลเท่านั้น และเมื่อมีการยกเลิกการหมุนใบจองถูกปิดการใช้งานในโปรเซสเซอร์ท้องถิ่น
Neelansh Mittal

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