อะไรคือความแตกต่างพื้นฐานระหว่างเซมาฟอร์และสปินล็อค?
เมื่อใดที่เราจะใช้เซมาฟอร์บนสปินล็อค?
อะไรคือความแตกต่างพื้นฐานระหว่างเซมาฟอร์และสปินล็อค?
เมื่อใดที่เราจะใช้เซมาฟอร์บนสปินล็อค?
คำตอบ:
Spinlock และ semaphore แตกต่างกันโดยส่วนใหญ่ในสี่สิ่ง:
1. สิ่งที่พวกเขาspinlockเป็นหนึ่งในการดำเนินงานเป็นไปได้ของการล็อคคือหนึ่งที่มีการดำเนินการโดยรอไม่ว่าง ( "ปั่น") สัญญาณเป็นลักษณะทั่วไปของการล็อก (หรือในทางกลับกันการล็อกเป็นกรณีพิเศษของเซมาฟอร์) โดยปกติแต่ไม่จำเป็นว่า spinlocks จะใช้ได้ภายในกระบวนการเดียวเท่านั้นในขณะที่ semaphores สามารถใช้เพื่อซิงโครไนซ์ระหว่างกระบวนการต่างๆได้เช่นกัน
การล็อกทำงานเพื่อการยกเว้นซึ่งกันและกันนั่นคือทีละเธรดสามารถรับการล็อกและดำเนินการต่อด้วย "ส่วนสำคัญ" ของรหัส โดยปกตินี่หมายถึงรหัสที่แก้ไขข้อมูลบางส่วนที่ใช้ร่วมกันโดยหลายเธรด
เซมาฟอร์มีตัวนับและจะอนุญาตให้ตัวเองได้มาโดยเธรดหนึ่งหรือหลายเธรดขึ้นอยู่กับค่าที่คุณโพสต์ลงไปและ (ในการนำไปใช้งานบางอย่าง) ขึ้นอยู่กับว่าค่าสูงสุดที่อนุญาตคือเท่าใด
Insofar เราสามารถพิจารณาล็อคกรณีพิเศษของเซมาฟอร์ที่มีค่าสูงสุด 1
2. สิ่งที่พวกเขาทำ
ตามที่ระบุไว้ข้างต้น spinlock คือตัวล็อคดังนั้นกลไกการกีดกันซึ่งกันและกัน (อย่างเคร่งครัด 1 ถึง 1) ทำงานโดยการสืบค้นและ / หรือแก้ไขตำแหน่งหน่วยความจำซ้ำ ๆ โดยปกติจะเป็นแบบอะตอม ซึ่งหมายความว่าการได้มาซึ่ง Spinlock เป็นการดำเนินการที่ "ยุ่ง" ซึ่งอาจเผาผลาญวงจรของ CPU เป็นเวลานาน (อาจจะตลอดไป!) ในขณะที่ "ไม่มีอะไร"
แรงจูงใจหลักสำหรับแนวทางดังกล่าวคือความจริงที่ว่าสวิตช์บริบทมีค่าใช้จ่ายเทียบเท่ากับการหมุนสองสามร้อย (หรืออาจถึงพันครั้ง) ดังนั้นหากสามารถรับการล็อคได้โดยการเผาไหม้สองสามรอบซึ่งโดยรวมแล้วอาจเป็นอย่างดี มีประสิทธิภาพมากกว่า. นอกจากนี้สำหรับแอปพลิเคชันแบบเรียลไทม์อาจไม่สามารถบล็อกได้และรอให้ตัวกำหนดตารางเวลากลับมาหาพวกเขาในเวลาที่ไกลออกไปในอนาคต
ตรงกันข้ามสัญญาณไม่หมุนเลยหรือหมุนเพียงช่วงเวลาสั้น ๆ (เป็นการเพิ่มประสิทธิภาพเพื่อหลีกเลี่ยงค่าใช้จ่ายของ syscall) หากไม่สามารถหาเซมาฟอร์ได้มันจะบล็อกทำให้เสียเวลาของ CPU ไปยังเธรดอื่นที่พร้อมทำงาน แน่นอนว่านี่อาจหมายความว่าเวลาผ่านไปไม่กี่มิลลิวินาทีก่อนที่เธรดของคุณจะถูกกำหนดเวลาอีกครั้ง แต่ถ้าสิ่งนี้ไม่มีปัญหา (โดยปกติจะไม่เป็นเช่นนั้น) ก็อาจเป็นวิธีการที่มีประสิทธิภาพและประหยัด CPU มาก
3. พฤติกรรมของพวกเขาเมื่อมีความแออัด
เป็นความเข้าใจผิดที่พบบ่อยว่าสปินล็อกหรืออัลกอริทึมที่ไม่มีการล็อกนั้น "โดยทั่วไปเร็วกว่า" หรือมีประโยชน์เฉพาะสำหรับ "งานที่สั้นมาก" (โดยหลักการแล้วไม่ควรถือวัตถุซิงโครไนซ์ไว้นานกว่านั้น เกินความจำเป็นเลยทีเดียว)
ความแตกต่างที่สำคัญอย่างหนึ่งคือวิธีการวิธีการที่แตกต่างกันมีพฤติกรรมในการปรากฏตัวของความแออัด
โดยปกติระบบที่ออกแบบมาอย่างดีจะมีความแออัดต่ำหรือไม่มีเลย (ซึ่งหมายความว่าเธรดทั้งหมดไม่ได้พยายามล็อคในเวลาเดียวกันแน่นอน) ตัวอย่างเช่นโดยปกติจะไม่เขียนโค้ดที่ได้รับการล็อกจากนั้นโหลดข้อมูลที่บีบอัด zip ครึ่งเมกะไบต์จากเครือข่ายถอดรหัสและแยกวิเคราะห์ข้อมูลและสุดท้ายแก้ไขการอ้างอิงที่แชร์ (ต่อท้ายข้อมูลลงในคอนเทนเนอร์ ฯลฯ ) ก่อนที่จะปลดล็อค แต่หนึ่งจะได้รับการล็อคเพื่อวัตถุประสงค์ในการเข้าถึงทรัพยากรที่ใช้ร่วมกันเท่านั้น
เนื่องจากนี่หมายความว่ามีงานนอกส่วนที่สำคัญมากกว่าด้านในเป็นอย่างมากความเป็นไปได้ที่เธรดจะอยู่ในส่วนวิกฤตจึงค่อนข้างต่ำและมีเธรดเพียงไม่กี่เธรดที่ต่อสู้เพื่อการล็อคในเวลาเดียวกัน แน่นอนว่าทุก ๆ ครั้งจะมีเธรดสองเธรดที่จะได้รับการล็อคในเวลาเดียวกัน (หากไม่สามารถเกิดขึ้นได้คุณก็ไม่จำเป็นต้องล็อก!) แต่นี่เป็นข้อยกเว้นมากกว่ากฎในระบบที่ "แข็งแรง" .
ในกรณีเช่นนี้สปินล็อคมีประสิทธิภาพดีกว่าเซมาฟอร์อย่างมากเนื่องจากหากไม่มีความแออัดของการล็อกค่าใช้จ่ายในการรับสปิล็อกจะเป็นเพียงรอบโหลเมื่อเทียบกับหลายร้อย / หลายพันรอบสำหรับสวิตช์บริบทหรือ 10-20 ล้านรอบสำหรับการสูญเสีย ส่วนเวลาที่เหลือ
ในทางกลับกันเมื่อมีความแออัดสูงหรือหากมีการล็อคเป็นระยะเวลานาน (บางครั้งคุณก็ไม่สามารถช่วยได้!) Spinlock จะเผาผลาญวงจร CPU จำนวนมากเพื่อไม่ให้ทำอะไรเลย
เซมาฟอร์ (หรือ mutex) เป็นทางเลือกที่ดีกว่ามากในกรณีนี้เนื่องจากอนุญาตให้เธรดอื่นรันงานที่มีประโยชน์ในช่วงเวลานั้น หรือหากไม่มีเธรดอื่นที่มีประโยชน์ในการทำระบบปฏิบัติการจะเร่งความเร็วซีพียูและลดความร้อน / ประหยัดพลังงาน
นอกจากนี้ในระบบแกนเดียวสปินล็อคจะไม่มีประสิทธิภาพในการมีความแออัดของการล็อคเนื่องจากเธรดที่หมุนวนจะเสียเวลาโดยสิ้นเชิงในการรอการเปลี่ยนแปลงสถานะที่ไม่อาจเกิดขึ้นได้ (ไม่ใช่จนกว่าจะมีการกำหนดเธรดการปล่อยซึ่งไม่ใช่ ไม่เกิดขึ้นในขณะที่กำลังรอเธรดกำลังทำงานอยู่!) ดังนั้นเมื่อพิจารณาถึงความขัดแย้งในปริมาณเท่าใดก็ได้การได้มาซึ่งการล็อคจะใช้เวลาประมาณ 1 1/2 ครั้งในกรณีที่ดีที่สุด (สมมติว่าเธรดการปลดล็อกเป็นเธรดถัดไปที่กำหนดเวลาไว้) ซึ่งไม่ใช่พฤติกรรมที่ดีมาก
4. วิธีการใช้งานเซมา
ฟอร์ในปัจจุบันมักจะรวมsys_futex
อยู่ภายใต้ลินุกซ์ (เลือกได้ด้วยสปินล็อกที่ออกหลังจากพยายามเพียงไม่กี่ครั้ง)
โดยทั่วไปแล้ว Spinlock จะถูกนำไปใช้โดยใช้การดำเนินการของอะตอมและไม่ต้องใช้สิ่งใด ๆ ที่ระบบปฏิบัติการให้มา ในอดีตสิ่งนี้หมายถึงการใช้คำสั่งภายในคอมไพเลอร์หรือแอสเซมเบลอร์แบบไม่พกพา ในขณะเดียวกันทั้ง C ++ 11 และ C11 มีการดำเนินการของอะตอมเป็นส่วนหนึ่งของภาษาดังนั้นนอกเหนือจากความยากลำบากทั่วไปในการเขียนรหัสที่ไม่มีการล็อคที่พิสูจน์ได้แล้วตอนนี้คุณสามารถใช้รหัสที่ไม่มีการล็อคในแบบพกพาทั้งหมดและ (เกือบ) วิธีที่ไม่เจ็บปวด
ง่ายมากเซมาฟอร์คือวัตถุซิงโครไนซ์ที่ "ยอม" สปิล็อกคือสิ่งที่ 'ไม่ว่าง' (มีเซมาโฟร์อีกเล็กน้อยที่จะซิงโครไนซ์เธรดหลายเธรดซึ่งแตกต่างจาก mutex หรือการ์ดหรือมอนิเตอร์หรือส่วนที่สำคัญที่ปกป้องขอบเขตโค้ดจากเธรดเดียว)
คุณจะใช้เซมาฟอร์ในหลาย ๆ สถานการณ์ แต่ใช้สปิล็อกที่คุณจะล็อคในช่วงเวลาสั้น ๆ - มีค่าใช้จ่ายในการล็อคโดยเฉพาะอย่างยิ่งหากคุณล็อคมาก ในกรณีเช่นนี้อาจมีประสิทธิภาพมากขึ้นในการ Spinlock ในขณะที่รอให้ทรัพยากรที่ได้รับการป้องกันปลดล็อก เห็นได้ชัดว่ามีการตีประสิทธิภาพหากคุณหมุนนานเกินไป
โดยทั่วไปถ้าคุณหมุนนานกว่าควอนตัมเธรดคุณควรใช้เซมาฟอร์
เหนือสิ่งที่ Yoav Aviram และ gbjbaanb กล่าวประเด็นสำคัญอื่น ๆ ที่เคยเป็นก็คือคุณจะไม่ใช้สปินล็อคบนเครื่องซีพียูตัวเดียวในขณะที่สัญญาณจะเหมาะสมกับเครื่องดังกล่าว ทุกวันนี้คุณมักจะรู้สึกลำบากในการค้นหาเครื่องที่ไม่มีหลายคอร์หรือไฮเปอร์เธรดหรือเทียบเท่า แต่ในสถานการณ์ที่คุณมีซีพียูเพียงตัวเดียวคุณควรใช้เซมาโฟร์ (ฉันเชื่อว่าเหตุผลนั้นชัดเจนหาก CPU ตัวเดียวไม่ว่างรอให้สิ่งอื่นคลายการหมุนล็อค แต่กำลังทำงานบน CPU เพียงตัวเดียวการล็อกไม่น่าจะถูกคลายออกจนกว่ากระบวนการหรือเธรดปัจจุบันจะถูกตัดทอนโดย O / S ซึ่งอาจใช้เวลาสักครู่และไม่มีประโยชน์เกิดขึ้นจนกว่าใบจองจะเกิดขึ้น)
จาก Linux Device Drivers โดย Rubinni
ซึ่งแตกต่างจาก semaphores, spinlocks อาจถูกใช้ในรหัสที่ไม่สามารถนอนหลับได้เช่นตัวจัดการขัดจังหวะ
ฉันไม่ใช่ผู้เชี่ยวชาญด้านเคอร์เนล แต่มีบางประเด็น:
แม้แต่เครื่องยูนิโพรเซสเซอร์ก็สามารถใช้สปินล็อกได้หากเปิดใช้งานใบจองเคอร์เนลขณะคอมไพล์เคอร์เนล หากใบจองเคอร์เนลถูกปิดใช้งานการหมุนล็อค (บางที) จะขยายเป็นโมฆะคำสั่ง
นอกจากนี้เมื่อเราพยายามเปรียบเทียบ Semaphore กับ Spin-lock ฉันเชื่อว่าเซมาฟอร์หมายถึงสิ่งที่ใช้ในเคอร์เนลไม่ใช่แบบที่ใช้สำหรับ IPC (userland)
โดยทั่วไปจะใช้สปินล็อคหากส่วนวิกฤตมีขนาดเล็ก (เล็กกว่าค่าใช้จ่ายในการนอน / การตื่นนอน) และส่วนวิกฤตจะไม่เรียกสิ่งที่สามารถนอนหลับได้! จะใช้สัญญาณหากส่วนวิกฤตใหญ่กว่าและสามารถนอนหลับได้
รามัญชโลตรา.
Spinlock หมายถึงการใช้งานการล็อคระหว่างเธรดโดยใช้คำแนะนำในการประกอบขึ้นอยู่กับเครื่อง (เช่นการทดสอบและการตั้งค่า) เรียกว่าสปินล็อกเนื่องจากเธรดจะรอในการวนซ้ำ ("สปิน") ตรวจสอบซ้ำ ๆ จนกว่าจะล็อกพร้อมใช้งาน Spinlocks ใช้แทน mutexes ซึ่งเป็นอุปกรณ์อำนวยความสะดวกที่จัดทำโดยระบบปฏิบัติการ (ไม่ใช่ CPU) เนื่องจาก Spinlocks ทำงานได้ดีกว่าหากถูกล็อคเป็นระยะเวลาสั้น ๆ
Semaphor เป็นสิ่งอำนวยความสะดวกที่จัดหาโดยระบบปฏิบัติการสำหรับ IPC ดังนั้นจึงมีจุดประสงค์หลักคือการสื่อสารระหว่างกระบวนการ การเป็นสิ่งอำนวยความสะดวกที่จัดทำโดยระบบปฏิบัติการประสิทธิภาพจะไม่ดีเท่ากับสปินล็อคสำหรับการล็อกระหว่างกัน (แม้ว่าจะเป็นไปได้) Semaphores ดีกว่าสำหรับการล็อคเป็นระยะเวลานานขึ้น
ที่กล่าวว่า - การใช้ splinlocks ในการประกอบเป็นเรื่องยุ่งยากและไม่สามารถพกพาได้
ฉันต้องการเพิ่มข้อสังเกตของฉันโดยทั่วไปมากขึ้นและไม่เฉพาะกับ Linux มากนัก
ขึ้นอยู่กับสถาปัตยกรรมหน่วยความจำและความสามารถของโปรเซสเซอร์คุณอาจต้องใช้สปินล็อกเพื่อใช้เซมาฟอร์บนระบบมัลติคอร์หรือระบบมัลติโปรเซสเซอร์เนื่องจากในระบบดังกล่าวเงื่อนไขการแย่งชิงอาจเกิดขึ้นเมื่อเธรด / กระบวนการตั้งแต่สองเธรดขึ้นไป เพื่อรับสัญญาณ
ใช่หากสถาปัตยกรรมหน่วยความจำของคุณมีการล็อกส่วนหน่วยความจำโดยคอร์ / โปรเซสเซอร์หนึ่งตัวซึ่งทำให้การเข้าถึงอื่น ๆ ทั้งหมดล่าช้าและหากโปรเซสเซอร์ของคุณเสนอการทดสอบและตั้งค่าคุณสามารถใช้เซมาฟอร์ได้โดยไม่ต้องใช้สปินล็อค (แต่อย่างระมัดระวัง! )
อย่างไรก็ตามเนื่องจากระบบมัลติคอร์ที่เรียบง่าย / ราคาถูกได้รับการออกแบบมา (ฉันกำลังทำงานในระบบฝังตัว) สถาปัตยกรรมหน่วยความจำบางส่วนไม่รองรับคุณสมบัติแบบมัลติคอร์ / มัลติโปรเซสเซอร์ดังกล่าวเพียงทดสอบและตั้งค่าหรือเทียบเท่า จากนั้นการนำไปใช้งานอาจเป็นดังนี้:
การปล่อยสัญญาณจะต้องดำเนินการดังต่อไปนี้:
ใช่และสำหรับเซมาโฟร์ไบนารีอย่างง่ายในระดับระบบปฏิบัติการนั้นจะเป็นไปได้ที่จะใช้เฉพาะสปินล็อคแทน แต่เฉพาะในกรณีที่โค้ดส่วนที่จะป้องกันนั้นมีขนาดเล็กมาก
อย่างที่กล่าวไว้ก่อนหน้านี้ถ้าคุณใช้ระบบปฏิบัติการของคุณเองและเมื่อไหร่อย่าลืมระมัดระวัง การแก้ไขข้อผิดพลาดดังกล่าวเป็นเรื่องสนุก (ความคิดเห็นของฉันไม่ได้แชร์โดยคนจำนวนมาก) แต่ส่วนใหญ่น่าเบื่อและยากมาก
"mutex" (หรือ "การล็อกการยกเว้นร่วมกัน") เป็นสัญญาณว่ากระบวนการแบบอะซิงโครนัสตั้งแต่สองกระบวนการขึ้นไปสามารถใช้เพื่อสงวนทรัพยากรที่ใช้ร่วมกันสำหรับการใช้งานเฉพาะ กระบวนการแรกที่ได้รับความเป็นเจ้าของ "mutex" ยังได้รับความเป็นเจ้าของทรัพยากรที่ใช้ร่วมกัน กระบวนการอื่น ๆ ต้องรอให้กระบวนการแรกปลดปล่อยความเป็นเจ้าของ "mutex" ก่อนจึงจะพยายามได้รับ
การล็อกแบบดั้งเดิมที่พบบ่อยที่สุดในเคอร์เนลคือสปินล็อก Spinlock เป็นตัวล็อคแบบตัวยึดเดียวที่เรียบง่ายมาก หากกระบวนการพยายามที่จะได้รับสปิล็อกและไม่สามารถใช้งานได้กระบวนการจะพยายามต่อไป (หมุน) จนกว่าจะได้รับการล็อก ความเรียบง่ายนี้ทำให้เกิดการล็อกขนาดเล็กและรวดเร็ว
Spinlock จะใช้ในกรณีที่คุณค่อนข้างมั่นใจว่าผลลัพธ์ที่คุณคาดหวังจะเกิดขึ้นในไม่ช้าก่อนที่เวลาดำเนินการของเธรดของคุณจะหมด
ตัวอย่าง: ในโมดูลไดรเวอร์อุปกรณ์ไดรเวอร์จะเขียน "0" ในการลงทะเบียนฮาร์ดแวร์ R0 และตอนนี้ต้องรอให้รีจิสเตอร์ R0 นั้นกลายเป็น 1 H / W อ่าน R0 และทำงานบางอย่างและเขียน "1" ใน R0 โดยทั่วไปจะเร็ว (เป็นไมโครวินาที) ตอนนี้การปั่นทำได้ดีกว่าการนอนหลับและถูกขัดจังหวะด้วย H / W แน่นอนว่าขณะปั่น H / W ล้มเหลวต้องได้รับการดูแล!
ไม่มีเหตุผลอย่างยิ่งที่แอปพลิเคชันผู้ใช้จะหมุน มันไม่สมเหตุสมผล คุณกำลังจะหมุนเพื่อให้เหตุการณ์บางอย่างเกิดขึ้นและเหตุการณ์นั้นจะต้องเสร็จสิ้นโดยแอปพลิเคชันระดับผู้ใช้อื่นซึ่งไม่รับประกันว่าจะเกิดขึ้นภายในกรอบเวลาที่รวดเร็ว ดังนั้นฉันจะไม่หมุนเลยในโหมดผู้ใช้ ฉันควรนอนหลับ () หรือ mutexlock () หรือ semaphore lock () ในโหมดผู้ใช้
จากสิ่งที่เป็นความแตกต่างระหว่างสปินและล็อคสัญญาณหรือไม่ โดยMaciej Piechotka :
ทั้งสองจัดการทรัพยากรที่ จำกัด ก่อนอื่นฉันจะอธิบายความแตกต่างระหว่างเซมาฟอร์ไบนารี (mutex) และสปินล็อค
ล็อคสปินทำการรออย่างไม่ว่าง - กล่าวคือมันยังคงวนอยู่
ในขณะที่ (try_acquire_resource ()); ... ปล่อย();มันทำการล็อค / ปลดล็อคที่มีน้ำหนักเบามาก แต่ถ้าเธรดการล็อคจะถูกจับจองโดยคนอื่นซึ่งจะพยายามเข้าถึงทรัพยากรเดียวกันอันที่สองก็จะพยายามที่จะได้รับทรัพยากรจนกว่ามันจะหมด CPU quanta
ในทางกลับกัน mutex ทำตัวเหมือน:ถ้า (! try_lock ()) { add_to_waiting_queue (); รอ(); } ... กระบวนการ * p = get_next_process_from_waiting_queue (); p-> ปลุก ();ดังนั้นหากเธรดพยายามที่จะรับทรัพยากรที่ถูกบล็อกมันจะถูกระงับจนกว่าจะสามารถใช้งานได้ การล็อก / ปลดล็อกนั้นหนักกว่ามาก แต่การรอนั้น 'ฟรี' และ 'ยุติธรรม'
Semaphoreคือการล็อกที่อนุญาตให้ใช้หลายครั้ง (ทราบจากการกำหนดค่าเริ่มต้น) จำนวนครั้งตัวอย่างเช่น 3 เธรดได้รับอนุญาตให้เก็บทรัพยากรแบบเดียวกันโดยปกติ แต่ไม่เกิน ใช้เป็นตัวอย่างในปัญหาผู้ผลิต / ผู้บริโภคหรือโดยทั่วไปในคิว:
P (resources_sem) ทรัพยากร = resources.pop () ... resources.push (ทรัพยากร) V (resources_sem)
สปินล็อคสามารถถือได้โดยกระบวนการเดียวในขณะที่สัญญาณสามารถถูกยึดโดยกระบวนการหนึ่งหรือหลายกระบวนการ ล็อคการหมุนรอจนกว่ากระบวนการจะคลายล็อกจากนั้นจึงได้รับการล็อก สัญญาณคือการล็อคการนอนหลับคือรอและเข้านอน
spin_trylock
ซึ่งจะส่งคืนทันทีพร้อมกับรหัสข้อผิดพลาดหากไม่สามารถรับการล็อกได้ การหมุนล็อคไม่ได้รุนแรงเสมอไป แต่การใช้spin_trylock
ต้องใช้เพื่อให้แอปพลิเคชันได้รับการออกแบบอย่างเหมาะสม (อาจเป็นคิวของการดำเนินการที่รอดำเนินการและที่นี่เลือกรายการถัดไปโดยปล่อยให้เป็นจริงในคิว)