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

อ่าน 5 นาที

อัลกอริทึมเบเกอรี่ของแลมพอร์ต

อัลกอริธึมการควบคุมการทำงานพร้อมกัน/ใช้วันที่ dmy ตั้งแต่เดือนตุลาคม 2022

อัลกอริทึมเบเกอรี่ของแลมพอร์ทเป็นอัลกอริทึม คอมพิวเตอร์ ที่คิดค้นโดยนักวิทยาศาสตร์คอมพิวเตอร์เลสลี

อัลกอริทึมเบเกอรี่ของแลมพอร์ต

( เรียนรู้วิธีและเวลาในการลบข้อความนี้ )

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

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

อัลกอริทึม

การเปรียบเทียบ

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

ตามหลักการเปรียบเทียบแล้ว "ลูกค้า" เปรียบเสมือนเธรด ซึ่งระบุด้วยตัวอักษรi ที่ ได้มาจากตัวแปรส่วนกลาง

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

ส่วนวิกฤต

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

เมื่อเธรดต้องการเข้าสู่ส่วนวิกฤต (critical section) มันจะต้องตรวจสอบว่าถึงคิวของมันแล้วหรือไม่ โดยจะต้องตรวจสอบหมายเลขnของเธรดอื่นๆ ทุกเธรดเพื่อให้แน่ใจว่าหมายเลข n ของมันน้อยที่สุด ในกรณีที่เธรดอื่นมีหมายเลขเดียวกัน เธรดที่มีหมายเลข n น้อยที่สุดจะได้เข้าสู่ส่วนวิกฤตก่อน

การเปรียบเทียบระหว่างเธรดaและbสามารถเขียนเป็นรหัสเทียม ได้ ใน รูปแบบดังนี้:

// ให้ n เป็นหมายเลขลูกค้าสำหรับกระทู้aและ // i  - หมายเลขเกลียวสำหรับเกลียวaจากนั้น (n  , i  ) < (n  , i  )

ซึ่งเทียบเท่ากับ:

(n  < n  ) หรือ ((n  == n  ) และ (i  < i  ))

เมื่อเธรดทำงานที่สำคัญเสร็จสิ้นแล้ว มันจะลบหมายเลขของตัวเองออกและเข้าสู่ส่วนที่ไม่สำคัญ

ส่วนที่ไม่สำคัญ

ส่วนที่ไม่สำคัญคือส่วนของโค้ดที่ไม่ต้องการการเข้าถึงแบบพิเศษ มันเป็นส่วนของการคำนวณเฉพาะเธรดที่ไม่รบกวนทรัพยากรและการทำงานของเธรดอื่นๆ

ส่วนนี้เปรียบเสมือนการกระทำที่เกิดขึ้นหลังจากซื้อของเสร็จ เช่น การใส่เงินทอนกลับเข้าไปในกระเป๋าเงิน

การนำอัลกอริทึมไปใช้

คำจำกัดความ

ในบทความต้นฉบับของ Lamport ตัวแปรขา เข้าเรียกว่าการเลือกและมีเงื่อนไขดังต่อไปนี้:

  • คำที่เลือก [i] และหมายเลข [i] อยู่ในหน่วยความจำของกระบวนการ i และเริ่มต้นเป็นศูนย์
  • ช่วงค่าของตัวเลข [i] นั้นไม่มีขอบเขต
  • กระบวนการอาจล้มเหลวได้ตลอดเวลา เราสมมติว่าเมื่อมันล้มเหลว มันจะเปลี่ยนไปสู่ส่วนที่ไม่สำคัญทันทีและหยุดทำงาน จากนั้นอาจมีช่วงเวลาที่การอ่านจากหน่วยความจำให้ค่าที่ไม่แน่นอน ในที่สุด การอ่านใดๆ จากหน่วยความจำจะต้องให้ค่าเป็นศูนย์

ตัวอย่างโค้ด

รหัสเทียม

ในตัวอย่างนี้ เธรดทั้งหมดจะเรียกใช้ฟังก์ชัน "main" เดียวกัน คือThreadในแอปพลิเคชันจริง เธรดต่างๆ มักจะมีฟังก์ชัน "main" ที่แตกต่างกัน

โปรดทราบว่าเช่นเดียวกับในเอกสารต้นฉบับ เธรดจะตรวจสอบตัวเองก่อนเข้าสู่ส่วนวิกฤต เนื่องจากเงื่อนไขของลูปจะประเมินเป็นเท็จจึงไม่ทำให้เกิดความล่าช้ามากนัก

// การประกาศและการกำหนดค่าเริ่มต้นของตัวแปรส่วนกลางกำลังป้อน: อาร์เรย์[ 1. . NUM_THREADS ] ของbool = { false };หมายเลข: อาร์เรย์[ 1. . NUM_THREADS ] ของจำนวนเต็ม= { 0 };ล็อก( จำนวนเต็มi ) {ป้อน[ i ] = true ;Number [ i ] = 1 + max ( Number [ 1 ], ..., Number [ NUM_THREADS ]);ป้อน[ i ] = false ;สำหรับ( จำนวนเต็มj = 1 ; j <= NUM_THREADS ; j ++ ) {// รอจนกว่าเธรด j จะได้รับหมายเลข:ในขณะที่( เข้าสู่[ j ]) { /* ไม่มีอะไร */ }// รอจนกว่าเธรดทั้งหมดที่มีหมายเลขน้อยกว่าหรือเหมือนกันจะเสร็จสิ้น// หมายเลข แต่มีลำดับความสำคัญสูงกว่า ทำงานให้เสร็จ:ในขณะที่(( Number [ j ] != 0 ) และ(( Number [ j ], j ) < ( Number [ i ], i ))) { /* ไม่มีอะไร */ }}}ปลดล็อก( จำนวนเต็มi ) {หมายเลข[ i ] = 0 ;}เธรด( จำนวนเต็มi ) {ในขณะที่( จริง) {ล็อก( i );// ส่วนสำคัญอยู่ตรงนี้...ปลดล็อก( i );// ส่วนที่ไม่สำคัญ...}}

แต่ละเธรดจะเขียนข้อมูลลงในพื้นที่จัดเก็บของตนเองเท่านั้น การอ่านข้อมูลจะใช้พื้นที่ร่วมกัน สิ่งที่น่าทึ่งคืออัลกอริทึมนี้ไม่ได้สร้างขึ้นบนพื้นฐานของการดำเนินการ "อะตอมิก" ระดับต่ำกว่า เช่นการเปรียบเทียบและสลับ (compare-and-swap ) การพิสูจน์ดั้งเดิมแสดงให้เห็นว่าสำหรับการอ่านและการเขียนที่ทับซ้อนกันในเซลล์จัดเก็บเดียวกัน การเขียนเท่านั้นที่จะต้องถูกต้องการดำเนินการอ่านสามารถส่งคืนค่าใดก็ได้ ดังนั้นอัลกอริทึมนี้จึงสามารถนำไปใช้ในการป้องกันการกีดกันร่วมกัน (mutual exclusion) บนหน่วยความจำที่ขาดกลไกการซิงโครไนซ์ เช่น ดิสก์ SCSI แบบง่าย ที่ใช้ร่วมกันระหว่างคอมพิวเตอร์สองเครื่อง

ความจำเป็นของตัวแปรEnteringอาจไม่ชัดเจนนัก เนื่องจากไม่มี 'การล็อก' ใดๆ ในบรรทัดที่ 7 ถึง 13 อย่างไรก็ตาม สมมติว่าตัวแปรนี้ถูกลบออกไป และมีสองกระบวนการคำนวณค่าเดียวกันNumber[i]หากกระบวนการที่มีลำดับความสำคัญสูงกว่าถูกขัดจังหวะก่อนที่จะตั้งค่าNumber[i]ตัวแปร Entering กระบวนการที่มีลำดับความสำคัญต่ำกว่าจะเห็นว่ากระบวนการอื่นมีค่าเป็นศูนย์ และเข้าสู่ส่วนวิกฤต ต่อมา กระบวนการที่มีลำดับความสำคัญสูงกว่าจะเพิกเฉยต่อค่าที่เท่ากันNumber[i]สำหรับกระบวนการที่มีลำดับความสำคัญต่ำกว่า และเข้าสู่ส่วนวิกฤตเช่นกัน ผลก็คือ สองกระบวนการสามารถเข้าสู่ส่วนวิกฤตได้พร้อมกัน อัลกอริทึม Bakery ใช้ ตัวแปร Entering เพื่อทำให้การกำหนดค่าในบรรทัดที่ 6 ดูเหมือนว่าเป็นการทำงานแบบอะตอมิก กล่าวคือ กระบวนการiจะไม่มีวันเห็นค่าเท่ากับศูนย์สำหรับกระบวนการjที่กำลังจะเลือกค่าเดียวกันกับi

เมื่อนำรหัสเทียมไปใช้ในระบบประมวลผลเดียวหรือภายใต้การทำงานแบบมัลติทาสกิ้งแบบร่วมมือกันควรแทนที่ส่วน "ไม่ทำอะไรเลย" ด้วยโค้ดที่แจ้งให้ระบบปฏิบัติการสลับไปยังเธรดถัดไปทันที ส่วนประกอบพื้นฐานนี้มักเรียกว่า `sudo-to- yieldthread`

อัลกอริทึมเบเกอรี่ของ Lamport ถือว่า โมเดลหน่วยความจำ มีความสอดคล้องตามลำดับภาษาสมัยใหม่หรือโปรเซสเซอร์มัลติคอร์จำนวนน้อยมากหรือแทบไม่มีเลยที่ใช้โมเดลหน่วยความจำดังกล่าว ดังนั้น การใช้งานอัลกอริทึมอย่างถูกต้องมักจะต้องแทรกรั้วเพื่อป้องกันการจัดลำดับใหม่[ 1 ]

รหัสPlusCal

เรากำหนดให้ N เป็นจำนวนกระบวนการ และเราถือว่า N เป็นจำนวนธรรมชาติ

ค่าคงที่ N สมมติว่า N อยู่ใน Nat 

เรากำหนดให้ P เป็นเซตของกระบวนการ {1, 2, ... , N}

P == 1..N 

ตัวแปร num และ flag ถูกประกาศเป็นตัวแปรส่วนกลาง (global)

--อัลกอริทึม AtomicBakery { ตัวแปร num = [i \in P |-> 0], flag = [i \in P |-> FALSE]; 

ข้อความต่อไปนี้จะถือว่าLL(j, i)เป็นจริงก็ต่อเมื่อ<<num[j], j>>น้อยกว่าหรือเท่ากับ <<num [ i], i>> ตาม ลำดับพจนานุกรมปกติ

กำหนด { LL(j, i) == \/ num[j] < num[i] \/ /\ num[i] = num[j] /\ j =< i } 

สำหรับแต่ละองค์ประกอบใน P จะมีกระบวนการที่มีตัวแปรโลคอล unread, max และ nxt ขั้นตอนระหว่างป้ายกำกับที่ต่อเนื่องกัน p1, ..., p7, cs ถือว่าเป็นอะตอมิก คำสั่งที่มีการกำหนดค่า id ให้กับองค์ประกอบที่เลือกแบบไม่แน่นอนจากเซต S แล้วจึงดำเนินการส่วน body ขั้นตอนที่มีคำสั่ง await expr จะสามารถดำเนินการได้ก็ต่อเมื่อค่าของ expr เป็นTRUEเท่านั้น(x \in S) { body }

กระบวนการ (p ∈ P) ตัวแปรที่ยังไม่ได้อ่านในเซตย่อย P สูงสุดในแนท nxt \in P; { p1: ในขณะที่ (TRUE) { ยังไม่ได้อ่าน := P \ {self} ; สูงสุด := 0; flag[self] := TRUE; p2: ในขณะที่ (ยังไม่ได้อ่าน # {}) { ด้วย (i \in ยังไม่ได้อ่าน) { ยังไม่ได้อ่าน := ยังไม่ได้อ่าน \ {i}; ถ้า (num[i] > max) { max := num[i]; } } }; p3: num[self] := max + 1; p4: flag[self] := FALSE; ยังไม่ได้อ่าน := P \ {self} ; p5: ในขณะที่ (ยังไม่ได้อ่าน # {}) { with (i \in unread) { nxt := i ; }; await ~ flag[nxt]; p6: await \/ num[nxt] = 0 \/ LL(self, nxt) ; ยังไม่ได้อ่าน := ยังไม่ได้อ่าน \ {nxt}; } ; cs: ข้าม ; \* ส่วนวิกฤต; p7: num[self] := 0; }} } 

โค้ด Java

เราใช้คลาส AtomicIntegerArray ไม่ใช่เพราะการทำงานแบบอะตอมิกในตัว แต่เพราะเมธอด get และ set ของมันทำงานเหมือนกับการอ่านและเขียนแบบ volatile ซึ่งภายใต้โมเดลหน่วยความจำของ Javaจะทำให้มั่นใจได้ว่าการเขียนจะปรากฏให้เห็นทันทีในทุกเธรด

AtomicIntegerArray ticket = new AtomicIntegerArray ( threads ); // ตั๋วสำหรับเธรดในบรรทัด, n - จำนวนเธรด// Java จะกำหนดค่าเริ่มต้นให้กับแต่ละองค์ประกอบของ 'ticket' เป็น 0AtomicIntegerArray entering = new AtomicIntegerArray ( threads ); // 1 เมื่อเธรดเข้าสู่บรรทัด// Java จะกำหนดค่าเริ่มต้นให้กับแต่ละองค์ประกอบของ 'entering' เป็น 0public void lock ( int pid ) // รหัสเธรด{เข้าสู่. set ( pid , 1 );ค่าสูงสุด= 0 ;สำหรับ( int i = 0 ; i < threads ; i ++ ){int current = ticket.get ( i ) ;ถ้า( ปัจจุบัน> ค่าสูงสุด){ค่าสูงสุด= ปัจจุบัน;}}ตั้งค่าตั๋ว( pid , 1 + max );เข้าสู่. set ( pid , 0 );สำหรับ( int i = 0 ; i < ticket.length ( ) ; ++ i ){ถ้า( i != pid ){ในขณะที่( entering.get ( i ) == 1 ) { Thread.yield ( ) ; } // รอจนกว่าเธรดอื่นจะเลือกตั๋วในขณะที่( ticket.get ( i ) ! = 0 และ( ticket.get ( i ) < ticket.get ( pid ) ||( ticket.get ( i ) == ticket.get ( pid ) && i < pid ) ) ){ Thread.yield ( ) ; }}}// ส่วนสำคัญอยู่ตรงนี้...}public void unlock ( int pid ){ตั้งค่าตั๋ว( pid , 0 ) ;}

ดูเพิ่มเติม

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

สรุปเนื้อหา

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

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

อัลกอริทึมเบเกอรี่ของแลมพอร์ทเป็นอัลกอริทึม คอมพิวเตอร์ ที่คิดค้นโดยนักวิทยาศาสตร์คอมพิวเตอร์เลสลี

การเปรียบเทียบ

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

ส่วนวิกฤต

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

ส่วนที่ไม่สำคัญ

ส่วนที่ไม่สำคัญคือส่วนของโค้ดที่ไม่ต้องการการเข้าถึงแบบพิเศษ มันเป็นส่วนของการคำนวณเฉพาะเธรดที่ไม่รบกวนทรัพยากรและการทำงานของเธรดอื่นๆ