กลับไปหน้าบทความ

อ่าน 2 นาที

อัลกอริทึมของเดกเกอร์

อัลกอริธึมการควบคุมการทำงานพร้อมกัน/เอ็ดส์เกอร์ ดับเบิลยู. ไดจ์กสตรา

อัลกอริทึมของ Dekkerเป็นวิธีแก้ปัญหาที่ถูกต้องวิธีแรกที่ทราบกันดีสำหรับ ปัญหา การกีดกันร่วมกันในการเขียนโปรแกรมแบบขนานซึ่งกระบวนการต่างๆ...

อัลกอริทึมของเดกเกอร์

อัลกอริทึมของ Dekkerเป็นวิธีแก้ปัญหาที่ถูกต้องวิธีแรกที่ทราบกันดีสำหรับ ปัญหา การกีดกันร่วมกันในการเขียนโปรแกรมแบบขนานซึ่งกระบวนการต่างๆ สื่อสารกันผ่านหน่วยความจำที่ใช้ร่วมกันเท่านั้น วิธีแก้ปัญหานี้ได้รับการระบุว่าเป็นผลงานของนักคณิตศาสตร์ชาวดัตช์Th. J. DekkerโดยEdsger W. Dijkstraในเอกสารที่ยังไม่ได้ตีพิมพ์เกี่ยวกับการอธิบายกระบวนการแบบลำดับ[ 1 ]และต้นฉบับของเขาเกี่ยวกับกระบวนการแบบลำดับที่ร่วมมือกัน [ 2 ] วิธีนี้ช่วยให้เธรดสองเธรดสามารถใช้ทรัพยากรแบบใช้ครั้งเดียวร่วมกันได้โดยไม่มีข้อขัดแย้ง โดยใช้เพียงหน่วยความจำที่ใช้ร่วมกันสำหรับการสื่อสาร

อัลกอริทึมนี้หลีกเลี่ยงการสลับกันอย่างเคร่งครัดของอัลกอริทึมการผลัดกันเล่นแบบง่ายๆ และเป็นหนึ่งใน อัลกอริทึม การกีดกันซึ่งกันและกัน ชุดแรกๆ ที่ถูกคิดค้นขึ้น

ภาพรวม

หากกระบวนการสองกระบวนการพยายามเข้าสู่ส่วนวิกฤตพร้อมกัน อัลกอริทึมจะอนุญาตให้เพียงกระบวนการเดียวเข้าไป โดยพิจารณาจากลำดับ ของ กระบวนการนั้น หากมีกระบวนการหนึ่งอยู่ในส่วนวิกฤตแล้ว กระบวนการอื่นจะรอจนกว่ากระบวนการแรกจะออกจากส่วนวิกฤต การดำเนินการนี้ทำได้โดยใช้แฟล็กสองตัว คือwants_to_enter[0]และwants_to_enter[1]ซึ่งระบุความตั้งใจที่จะเข้าสู่ส่วนวิกฤตของกระบวนการ 0 และ 1 ตามลำดับ และตัวแปรturnที่ระบุว่ากระบวนการใดมีลำดับความสำคัญมากกว่ากัน

อัลกอริทึมของเดกเกอร์

อัลกอริทึมของ Dekker สามารถแสดงเป็นรหัสเทียมได้ดังนี้[ 3 ]

 ตัวแปร wants_to_enter : อาร์เรย์ของค่าบูลีน 2 ค่า เทิร์น : จำนวนเต็ม wants_to_enter[0] ← false wants_to_enter[1] ← false หมุน ← 0 // หรือ 1 
p0: wants_to_enter[0] ← true ในขณะที่ต้องการเข้า[1] { ถ้าเทิร์น ≠ 0 { wants_to_enter[0] ← false ในขณะที่เทิร์น ≠ 0 { // รอแบบไม่ว่าง } wants_to_enter[0] ← true } } // ส่วนวิกฤต ... เลี้ยว ← 1 wants_to_enter[0] ← false // ส่วนที่เหลือ 
หน้า 1: wants_to_enter[1] ← true ในขณะที่ต้องการเข้า[0] { ถ้าเทิร์น ≠ 1 { wants_to_enter[1] ← false ในขณะที่เทิร์น ≠ 1 { // รอแบบไม่ว่าง } wants_to_enter[1] ← true } } // ส่วนวิกฤต ... เลี้ยว ← 0 wants_to_enter[1] ← false // ส่วนที่เหลือ 

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

อัลกอริทึมของ Dekker รับประกันการกีดกันซึ่งกันและกันการปราศจากภาวะติดตายและการปราศจากภาวะอดอยาก มาดูกันว่าทำไมคุณสมบัติสุดท้ายจึงเป็นจริง สมมติว่า p0 ติดอยู่ใน ลูป while wants_to_enter[1]ตลอดไป เนื่องจากปราศจากภาวะติดตาย ในที่สุด p1 จะดำเนินการไปยังส่วนวิกฤตและตั้งค่าturn = 0 (และค่าของ turn จะยังคงไม่เปลี่ยนแปลงตราบใดที่ p0 ยังไม่ดำเนินการต่อไป) ในที่สุด p0 จะหลุดออกจาก ลูป while turn ≠ 0 ภายใน (หากมันเคยติดอยู่ในนั้น) หลังจากนั้นมันจะตั้งค่าwants_to_enter[0]เป็น true และรอให้wants_to_enter[1]กลายเป็น false (เนื่องจากturn = 0มันจะไม่ทำการกระทำใดๆ ในลูป while) ครั้งต่อไปที่ p1 พยายามเข้าสู่ส่วนวิกฤต มันจะถูกบังคับให้ดำเนินการตามการกระทำในลูปwhile wants_to_enter[0]โดยเฉพาะอย่างยิ่ง ในที่สุดมันจะตั้งค่าwants_to_enter[1]เป็น false และติดอยู่ใน ลูป while turn ≠ 1 (เนื่องจาก turn ยังคงเป็น 0) ครั้งถัดไปที่การควบคุมผ่านไปยัง p0 มันจะออกจาก ลูป while wants_to_enter[1]และเข้าสู่ส่วนวิกฤต

หากอัลกอริทึมถูกแก้ไขโดยดำเนินการใน ลูป while wants_to_enter[1]โดยไม่ต้องตรวจสอบว่าturn = 0หรือไม่ ก็อาจเกิดภาวะอดอยากได้ ดังนั้นขั้นตอนทั้งหมดในอัลกอริทึมจึงจำเป็น

หมายเหตุ

ข้อดีอย่างหนึ่งของอัลกอริทึมนี้คือ ไม่จำเป็นต้องใช้ คำสั่ง ทดสอบและตั้งค่า พิเศษ (การอ่าน/แก้ไข/เขียนแบบอะตอมิก) ดังนั้นจึงสามารถใช้งานได้กับภาษาและสถาปัตยกรรมเครื่องคอมพิวเตอร์หลายประเภท ข้อเสียอย่างหนึ่งคือ จำกัดอยู่ที่สองกระบวนการ และใช้การรอแบบไม่หยุดนิ่ง (busy waiting) แทนการระงับกระบวนการ (process suspension) (การใช้การรอแบบไม่หยุดนิ่งบ่งชี้ว่ากระบวนการควรใช้เวลาภายในส่วนวิกฤตให้น้อยที่สุด)

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

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

นอกจากนี้ คอมไพเลอร์ที่ปรับแต่งประสิทธิภาพจำนวนมากสามารถทำการแปลงที่จะทำให้ขั้นตอนวิธีนี้ล้มเหลวได้ไม่ว่าจะเป็นแพลตฟอร์มใดก็ตาม ในหลายภาษา คอมไพเลอร์สามารถตรวจจับได้ว่าตัวแปรแฟล็กwants_to_enter[0]และwants_to_enter[1]ไม่เคยถูกเข้าถึงในลูป และสามารถลบการเขียนไปยังตัวแปรเหล่านั้นออกจากลูปได้ โดยใช้กระบวนการที่เรียกว่า การเคลื่อนย้ายโค้ดที่ไม่ขึ้นกับ ลูป (loop-invariant code motion ) นอกจากนี้ คอมไพเลอร์จำนวนมากยังสามารถตรวจจับได้ว่า ตัวแปร turnไม่เคยถูกแก้ไขโดยลูปภายใน และทำการแปลงที่คล้ายกัน ซึ่งอาจส่งผลให้เกิด ลูปอนันต์ได้ หากมีการแปลงอย่างใดอย่างหนึ่งเกิดขึ้น ขั้นตอนวิธีนี้จะล้มเหลวไม่ว่าจะเป็นสถาปัตยกรรมใดก็ตาม

เพื่อแก้ไขปัญหานี้ ตัวแปร volatileควรถูกทำเครื่องหมายว่าสามารถแก้ไขได้จากภายนอกขอบเขตของบริบทการทำงานปัจจุบัน ตัวอย่างเช่น ในภาษา C, C++, C# หรือ Java เราจะใส่คำอธิบายประกอบให้กับตัวแปรเหล่านี้ว่า 'volatile' อย่างไรก็ตาม โปรดทราบว่าแอตทริบิวต์ "volatile" ใน C/C++ รับประกันเพียงว่าคอมไพเลอร์จะสร้างโค้ดที่มีลำดับที่ถูกต้องเท่านั้น ไม่ได้รวมถึงตัวกั้นหน่วยความจำ ที่จำเป็น เพื่อรับประกันการทำงานของโค้ด ตามลำดับ ตัวแปรอะตอมิก ใน C++11สามารถใช้เพื่อรับประกันข้อกำหนดลำดับที่เหมาะสมได้ โดยค่าเริ่มต้น การดำเนินการกับตัวแปรอะตอมิกจะมีความสอดคล้องกันตามลำดับ ดังนั้นหากตัวแปร wants_to_enter และ turn เป็นอะตอมิก การใช้งานแบบง่ายๆ ก็จะ "ทำงานได้" หรืออีกทางหนึ่ง สามารถรับประกันลำดับได้โดยการใช้ fence แยกต่างหากอย่างชัดเจน โดยการดำเนินการ load และ store จะใช้ลำดับที่ผ่อนคลายกว่า

ดูเพิ่มเติม

ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Dekker%27s_algorithm&oldid=1341701407 "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ อัลกอริทึมของเดกเกอร์

อัลกอริทึมของ Dekkerเป็นวิธีแก้ปัญหาที่ถูกต้องวิธีแรกที่ทราบกันดีสำหรับ ปัญหา การกีดกันร่วมกันในการเขียนโปรแกรมแบบขนานซึ่งกระบวนการต่างๆ...

ภาพรวม

หากกระบวนการสองกระบวนการพยายามเข้าสู่ ส่วนวิกฤต พร้อมกัน อัลกอริทึมจะอนุญาตให้เพียงกระบวนการเดียวเข้าไป โดยพิจารณาจาก ลำดับ ของ กระบวนการนั้น หากมีกระบวนการหนึ่งอยู่ในส่วนวิกฤตแล้ว กระบวนการอื่นจะ รอ จนกว่ากระบวนการแรกจะออกจากส่วนวิกฤต...

หมายเหตุ

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

ดูเพิ่มเติม

อัลกอริทึมของ Eisenberg & McGuire อัลกอริทึมของปีเตอร์สัน อัลกอริทึมเบเกอรี่ของแลมพอร์ต อัลกอริทึมของ Szymański สัญญาณไฟ ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Dekker%27s_algorithm&oldid=1341701407 "