ตัวแปรตามเงื่อนไขเทียบกับเซมาฟอร์


111

เมื่อใดควรใช้เซมาฟอร์และเมื่อใดควรใช้ตัวแปรเงื่อนไข (CondVar)


1
ข้อมูลที่เกี่ยวข้องสามารถดูได้จากลิงค์stackoverflow.com/questions/4039899/…
Karthik Balaguru

คำตอบ:


207

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

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

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

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

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

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

สันนิษฐานว่าคุณจะมีด้ายที่อื่นที่ดึงสิ่งต่างๆออกจากคิว เมื่อคิวว่างก็สามารถโทรsyncVar.signal()ปลุกเธรดแบบสุ่มที่กำลังอยู่ในโหมดสลีปsyncVar.wait()(หรือโดยปกติจะมีวิธีsignalAll()หรือbroadcast()วิธีปลุกเธรดทั้งหมดที่รออยู่)

โดยทั่วไปฉันใช้ตัวแปรการซิงโครไนซ์เช่นนี้เมื่อฉันมีเธรดอย่างน้อยหนึ่งเธรดที่รออยู่ในเงื่อนไขเดียว (เช่นคิวว่าง)

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

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

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

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

wait()และsignal()วิธีการของตัวแปรตรงกันมีแนวโน้มที่จะถูกซ่อนไว้ภายในdown()และup()การดำเนินงานของสัญญาณ

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

นั่นไม่จำเป็นต้องเป็นคำอธิบายทางเทคนิคส่วนใหญ่ แต่นั่นเป็นสิ่งที่สมเหตุสมผลในหัวของฉัน


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

10
เพียงเพื่ออธิบายรายละเอียดเกี่ยวกับเหตุผลที่มีนี้ห่วงแทนง่ายถ้าในขณะที่: สิ่งที่เรียกว่าspurios ปลุก อ้างถึงบทความวิกิพีเดียนี้ : "สาเหตุหนึ่งที่ทำให้เกิดการปลุกแบบปลอม ๆ นั่นคือเธรดอาจถูกปลุกจากสถานะรอแม้ว่าเธรดจะไม่มีสัญญาณตัวแปรเงื่อนไขก็ตาม"
Vladislavs Burakovs

3
@VladislavsBurakovs จุดดี! ฉันคิดว่ามันมีประโยชน์เช่นกันสำหรับกรณีที่การแพร่ภาพปลุกเธรดมากกว่าที่มีทรัพยากร (เช่นการออกอากาศปลุก 3 เธรด แต่มีเพียง 2 รายการในคิว)
Brent Writes Code

ฉันหวังว่าฉันจะโหวตให้คุณตอบจนกว่าคิวจะเต็ม;) คำตอบที่สมบูรณ์แบบ รหัสนี้สามารถช่วยหา semaphores csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME

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

52

มาเปิดเผยสิ่งที่อยู่ภายใต้ประทุน

ตัวแปรตามเงื่อนไขคือการรอคิวซึ่งสนับสนุนการดำเนินการปิดกั้นการรอและการปลุกระบบกล่าวคือคุณสามารถใส่เธรดลงในคิวรอและตั้งค่าสถานะเป็นบล็อกและดึงเธรดออกจากเธรดและตั้งสถานะเป็นพร้อม

โปรดทราบว่าในการใช้ตัวแปรตามเงื่อนไขจำเป็นต้องมีองค์ประกอบอื่นอีกสององค์ประกอบ:

  • เงื่อนไข (โดยทั่วไปดำเนินการโดยการตรวจสอบธงหรือตัวนับ)
  • mutex ที่ปกป้องสภาพ

จากนั้นโปรโตคอลจะกลายเป็น

  1. รับ mutex
  2. ตรวจสอบสภาพ
  3. บล็อกและปล่อย mutex หากเงื่อนไขเป็นจริงมิฉะนั้นจะปล่อย mutex

Semaphore โดยพื้นฐานแล้วจะเป็นตัวนับ + mutex + รอคิว และสามารถใช้งานได้โดยไม่ต้องพึ่งพาภายนอก คุณสามารถใช้เป็น mutex หรือเป็นตัวแปรตามเงื่อนไขก็ได้

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


mutex สามารถมองได้ว่าเป็นตัวแปรเงื่อนไขเงื่อนไขคือไม่ว่าจะถูกระงับหรือไม่
宏杰李

18

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

น่าเสียดายที่คุณไม่สามารถใช้การซิงโครไนซ์กับ mutexes ได้นั่นคือเหตุผลที่เรามีตัวแปรเงื่อนไข โปรดสังเกตด้วยว่าด้วยตัวแปรเงื่อนไขคุณสามารถปลดล็อกเธรดที่รอทั้งหมดได้ในทันทีโดยใช้การปลดล็อกการออกอากาศ สิ่งนี้ไม่สามารถทำได้ด้วยเซมาโฟร์


9

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

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

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


1

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

ฉันมักจะเขียนโค้ดไปยังแบบฟอร์มการตรวจสอบ ในภาษาส่วนใหญ่ที่ฉันทำงานซึ่งลงมาถึง mutexes ตัวแปรเงื่อนไขและตัวแปรสถานะสำรองบางอย่าง แต่ semaphores ก็ทำงานได้เช่นกัน


2
นี่จะเป็นคำตอบที่ดีกว่าถ้าคุณอธิบายว่า "ฟอร์มการตรวจสอบ" คืออะไร
Steven Lu

0

ที่mutexและได้รับมาจากconditional variablessemaphore

  • สำหรับmutexการsemaphoreใช้สองสถานะ: 0, 1
  • สำหรับ เคาน์เตอร์ใช้condition variablessemaphore

พวกมันเหมือนน้ำตาลที่มีปฏิกิริยา


ในไลบรารี C ++ std พวกเขาเป็นออบเจ็กต์เขตทั้งหมดทั้งหมดถูกนำไปใช้โดยใช้ API เฉพาะแพลตฟอร์ม แน่นอนว่าสัญญาณจะยกเลิกการปิดกั้นจำนวนครั้งที่ส่งสัญญาณตัวแปรเงื่อนไขอาจถูกส่งสัญญาณหลายครั้ง แต่ปลดบล็อกเพียงครั้งเดียว นี่คือสาเหตุที่ wair ใช้ mutex เป็นพารามิเตอร์
doron
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.