เมื่อใดควรใช้เซมาฟอร์และเมื่อใดควรใช้ตัวแปรเงื่อนไข (CondVar)
เมื่อใดควรใช้เซมาฟอร์และเมื่อใดควรใช้ตัวแปรเงื่อนไข (CondVar)
คำตอบ:
ล็อคถูกใช้เพื่อการยกเว้นซึ่งกันและกัน เมื่อคุณต้องการให้แน่ใจว่าชิ้นส่วนของรหัสเป็นอะตอมให้ใส่กุญแจล็อคไว้รอบ ๆ ในทางทฤษฎีคุณสามารถใช้สัญญาณไบนารีเพื่อทำสิ่งนี้ได้ แต่เป็นกรณีพิเศษ
ตัวแปร 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()
การดำเนินงานของสัญญาณ
แน่นอนว่ามีสองตัวเลือกที่ทับซ้อนกัน มีหลายสถานการณ์ที่ทั้งเซมาฟอร์หรือตัวแปรเงื่อนไข (หรือชุดของตัวแปรเงื่อนไข) สามารถตอบสนองวัตถุประสงค์ของคุณได้ ทั้งเซมาโฟเรสและตัวแปรเงื่อนไขสัมพันธ์กับอ็อบเจ็กต์ล็อกที่ใช้เพื่อรักษาการยกเว้นซึ่งกันและกัน แต่จะมีฟังก์ชันพิเศษที่ด้านบนของล็อกสำหรับการซิงโครไนซ์เธรด ส่วนใหญ่ขึ้นอยู่กับคุณที่จะคิดว่าสิ่งใดเหมาะสมกับสถานการณ์ของคุณมากที่สุด
นั่นไม่จำเป็นต้องเป็นคำอธิบายทางเทคนิคส่วนใหญ่ แต่นั่นเป็นสิ่งที่สมเหตุสมผลในหัวของฉัน
มาเปิดเผยสิ่งที่อยู่ภายใต้ประทุน
ตัวแปรตามเงื่อนไขคือการรอคิวซึ่งสนับสนุนการดำเนินการปิดกั้นการรอและการปลุกระบบกล่าวคือคุณสามารถใส่เธรดลงในคิวรอและตั้งค่าสถานะเป็นบล็อกและดึงเธรดออกจากเธรดและตั้งสถานะเป็นพร้อม
โปรดทราบว่าในการใช้ตัวแปรตามเงื่อนไขจำเป็นต้องมีองค์ประกอบอื่นอีกสององค์ประกอบ:
จากนั้นโปรโตคอลจะกลายเป็น
Semaphore โดยพื้นฐานแล้วจะเป็นตัวนับ + mutex + รอคิว และสามารถใช้งานได้โดยไม่ต้องพึ่งพาภายนอก คุณสามารถใช้เป็น mutex หรือเป็นตัวแปรตามเงื่อนไขก็ได้
ดังนั้นสัญญาณสามารถถือว่าเป็นโครงสร้างที่ซับซ้อนกว่าตัวแปรเงื่อนไขในขณะที่แบบหลังมีน้ำหนักเบาและยืดหยุ่นกว่า
สามารถใช้ Semaphores เพื่อใช้การเข้าถึงตัวแปรแบบเอกสิทธิ์เฉพาะบุคคลได้อย่างไรก็ตามมีไว้เพื่อใช้ในการซิงโครไนซ์ ในทางกลับกัน Mutexes มีความหมายที่เกี่ยวข้องอย่างเคร่งครัดกับการยกเว้นซึ่งกันและกัน: เฉพาะกระบวนการที่ล็อกทรัพยากรเท่านั้นที่ได้รับอนุญาตให้ปลดล็อก
น่าเสียดายที่คุณไม่สามารถใช้การซิงโครไนซ์กับ mutexes ได้นั่นคือเหตุผลที่เรามีตัวแปรเงื่อนไข โปรดสังเกตด้วยว่าด้วยตัวแปรเงื่อนไขคุณสามารถปลดล็อกเธรดที่รอทั้งหมดได้ในทันทีโดยใช้การปลดล็อกการออกอากาศ สิ่งนี้ไม่สามารถทำได้ด้วยเซมาโฟร์
ตัวแปรเซมาฟอร์และเงื่อนไขมีความคล้ายคลึงกันมากและส่วนใหญ่จะใช้เพื่อวัตถุประสงค์เดียวกัน อย่างไรก็ตามมีข้อแตกต่างเล็กน้อยที่อาจทำให้ดีกว่า ตัวอย่างเช่นในการใช้การซิงโครไนซ์อุปสรรคคุณจะไม่สามารถใช้เซมาฟอร์ได้ แต่ตัวแปรเงื่อนไขนั้นเหมาะอย่างยิ่ง
การซิงโครไนซ์ Barrier คือเมื่อคุณต้องการให้เธรดทั้งหมดของคุณรอจนกว่าทุกคนจะมาถึงส่วนหนึ่งในฟังก์ชันเธรด สิ่งนี้สามารถนำไปใช้งานได้โดยมีตัวแปรคงที่ซึ่งเริ่มแรกคือค่าของเธรดทั้งหมดที่ลดลงโดยแต่ละเธรดเมื่อถึงอุปสรรคนั้น นี่หมายความว่าเราต้องการให้แต่ละเธรดเข้าสู่โหมดสลีปจนกว่าจะถึงอันสุดท้ายเซมาฟอร์จะทำตรงกันข้าม! ด้วยเซมาฟอร์แต่ละเธรดจะทำงานต่อไปและเธรดสุดท้าย (ซึ่งจะตั้งค่าเซมาฟอร์เป็น 0) จะเข้าสู่โหมดสลีป
ในทางกลับกันตัวแปรเงื่อนไขนั้นเหมาะอย่างยิ่ง เมื่อแต่ละเธรดถึงสิ่งกีดขวางเราจะตรวจสอบว่าตัวนับคงที่ของเราเป็นศูนย์หรือไม่ ถ้าไม่เราตั้งค่าเธรดให้เข้าสู่โหมดสลีปด้วยฟังก์ชันรอตัวแปรเงื่อนไข เมื่อเธรดสุดท้ายมาถึงสิ่งกีดขวางค่าตัวนับจะลดลงเป็นศูนย์และเธรดสุดท้ายนี้จะเรียกฟังก์ชันสัญญาณตัวแปรเงื่อนไขซึ่งจะปลุกเธรดอื่น ๆ ทั้งหมด!
ฉันไฟล์ตัวแปรเงื่อนไขภายใต้การซิงโครไนซ์มอนิเตอร์ โดยทั่วไปฉันเคยเห็น semaphores และจอภาพเป็นรูปแบบการซิงโครไนซ์ที่แตกต่างกันสองแบบ มีความแตกต่างระหว่างทั้งสองอย่างในแง่ของจำนวนข้อมูลสถานะที่ถูกเก็บไว้โดยเนื้อแท้และวิธีที่คุณต้องการสร้างโค้ดโมเดล - แต่จริงๆแล้วไม่มีปัญหาใด ๆ ที่สามารถแก้ไขได้โดยหนึ่ง แต่ไม่สามารถแก้ไขได้
ฉันมักจะเขียนโค้ดไปยังแบบฟอร์มการตรวจสอบ ในภาษาส่วนใหญ่ที่ฉันทำงานซึ่งลงมาถึง mutexes ตัวแปรเงื่อนไขและตัวแปรสถานะสำรองบางอย่าง แต่ semaphores ก็ทำงานได้เช่นกัน
ที่mutex
และได้รับมาจากconditional variables
semaphore
mutex
การsemaphore
ใช้สองสถานะ: 0, 1condition variables
semaphore
พวกมันเหมือนน้ำตาลที่มีปฏิกิริยา