ความแตกต่างระหว่างการหยุดชะงักและ livelock คืออะไร?


323

ใครบางคนช่วยอธิบายด้วยตัวอย่าง (ของรหัส) ความแตกต่างระหว่างการหยุดชะงักและlivelockคืออะไร?


คำตอบ:


398

นำมาจากhttp://en.wikipedia.org/wiki/Deadlock :

ในการคำนวณพร้อมกันDeadlockคือสถานะที่สมาชิกแต่ละคนของกลุ่มแอ็คชันกำลังรอให้สมาชิกคนอื่นปลดล็อค

livelockจะคล้ายกับการหยุดชะงักยกเว้นว่ารัฐของกระบวนการที่เกี่ยวข้องในการ livelock อย่างต่อเนื่องเปลี่ยนเรื่องไปอีกคนหนึ่งที่มีไม่มีความคืบหน้า Livelock เป็นกรณีพิเศษของความอดอยากทรัพยากร คำจำกัดความทั่วไประบุว่ากระบวนการเฉพาะนั้นไม่คืบหน้า

ตัวอย่างของโลกแห่งความเป็นจริงของ livelock เกิดขึ้นเมื่อคนสองคนพบกันในทางเดินแคบ ๆ และแต่ละคนพยายามที่จะสุภาพด้วยการย้ายออกไปเพื่อให้คนอื่นผ่าน แต่พวกเขาก็จบลงด้วยการโยกย้ายจากด้านหนึ่งไปอีกด้านหนึ่ง วิธีเดียวกันในเวลาเดียวกัน

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


8
ฉันพบแล้ว แต่พวกเขาไม่มีตัวอย่างที่นั่นอย่างที่คุณเห็นขอบคุณ
macindows

61
ฉันจะไม่ให้ตัวอย่างรหัส แต่พิจารณาสองกระบวนการแต่ละกระบวนการรอทรัพยากรอื่น ๆ มี แต่รอในลักษณะที่ไม่บล็อก เมื่อแต่ละคนเรียนรู้พวกเขาไม่สามารถดำเนินการต่อพวกเขาปล่อยทรัพยากรที่ค้างไว้และนอนหลับเป็นเวลา 30 วินาทีจากนั้นพวกเขาดึงทรัพยากรเดิมของพวกเขาตามด้วยการพยายามทรัพยากรที่กระบวนการอื่น ๆ ที่มีอยู่แล้วออกจากนั้น reaquired เนื่องจากทั้งสองกระบวนการพยายามที่จะรับมือ (แย่มาก) นี่คือความมีชีวิตชีวา
mah

4
คุณสามารถยกตัวอย่างให้ฉันได้ แต่ด้วยการหยุดชะงักขอบคุณล่วงหน้า
macindows

32
ตัวอย่างการหยุดชะงักนั้นง่ายกว่ามาก ... สมมติว่าสองกระบวนการ A และ B และแต่ละต้องการทรัพยากร r1 และทรัพยากร r2 สมมติว่า A ได้รับ (หรือมีอยู่แล้ว) r1 และ B ได้รับ (หรือมีอยู่แล้ว) r2 ตอนนี้แต่ละคนพยายามที่จะรับทรัพยากรอื่น ๆ โดยไม่ต้องหมดเวลา A ถูกบล็อกเพราะ B ถือ r2 และ B ถูกบล็อกเพราะ A ถือ r1 แต่ละกระบวนการถูกบล็อกและทำให้ไม่สามารถปล่อยทรัพยากรที่ต้องการอื่น ๆ ทำให้เกิดการหยุดชะงัก
mah

2
ภายในบริบทของหน่วยความจำธุรกรรมมีวิดีโอยอดเยี่ยมที่แสดงให้เห็นถึงการหยุดชะงักและ livelock: youtube.com/watch?v=_IxsOEEzf-c
BlackVegetable

78

Livelock

เธรดมักทำหน้าที่ตอบสนองต่อการกระทำของเธรดอื่น หากการดำเนินการของเธรดอื่นเป็นการตอบสนองต่อการดำเนินการของเธรดอื่นดังนั้นอาจส่งผลให้เกิดการหมุนวน

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

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

ในภาพนี้วงกลม (เธรดหรือกระบวนการ) จะพยายามให้พื้นที่กับอีกอันด้วยการเลื่อนไปทางซ้ายและขวา แต่พวกเขาไม่สามารถเคลื่อนไหวได้อีกต่อไป

ป้อนคำอธิบายรูปภาพที่นี่


ตัวอย่างโค้ดสำหรับ livelocks stackoverflow.com/questions/1036364/good-example-of-livelock
Yauhen Yakimovich

1
สิ่งนี้มีชื่อ อาจเป็นคำแสลง แต่ก็ยัง: schlumperdink : P
John Red

64

เนื้อหาและตัวอย่างทั้งหมดที่นี่มาจาก

ระบบปฏิบัติการ: หลักการภายในและการออกแบบ
William Stallings
8º Edition

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

ตัวอย่างเช่นพิจารณาสองกระบวนการ P1 และ P2 และสองทรัพยากร R1 และ R2 สมมติว่าแต่ละกระบวนการต้องการเข้าถึงทรัพยากรทั้งสองเพื่อดำเนินการส่วนหนึ่งของฟังก์ชัน จากนั้นเป็นไปได้ที่จะมีสถานการณ์ต่อไปนี้: ระบบปฏิบัติการกำหนด R1 ถึง P2 และ R2 ถึง P1 แต่ละกระบวนการกำลังรอหนึ่งในสองทรัพยากร จะไม่ปล่อยทรัพยากรที่มีอยู่แล้วจนกว่าจะได้รับทรัพยากรอื่นและดำเนินการฟังก์ชันที่ต้องใช้ทรัพยากรทั้งสอง กระบวนการทั้งสองถูกหยุดชะงัก

Livelock : สถานการณ์ที่กระบวนการสองกระบวนการขึ้นไปเปลี่ยนสถานะอย่างต่อเนื่องเพื่อตอบสนองต่อการเปลี่ยนแปลงในกระบวนการอื่นโดยไม่ต้องทำงานที่มีประโยชน์ใด ๆ :

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

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

ภาคผนวก A - หัวข้อที่น่าเชื่อถือ

ตัวอย่างการหยุดชะงัก

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

/* PROCESS 0 */
flag[0] = true;            // <- get lock 0
while (flag[1])            // <- is lock 1 free?
    /* do nothing */;      // <- no? so I wait 1 second, for example
                           // and test again.
                           // on more sophisticated setups we can ask
                           // to be woken when lock 1 is freed
/* critical section*/;     // <- do what we need (this will never happen)
flag[0] = false;           // <- releasing our lock

 /* PROCESS 1 */
flag[1] = true;
while (flag[0])
    /* do nothing */;
/* critical section*/;
flag[1] = false;

ตัวอย่าง Livelock

/* PROCESS 0 */
flag[0] = true;          // <- get lock 0
while (flag[1]){         
    flag[0] = false;     // <- instead of sleeping, we do useless work
                         //    needed by the lock mechanism
    /*delay */;          // <- wait for a second
    flag[0] = true;      // <- and restart useless work again.
}
/*critical section*/;    // <- do what we need (this will never happen)
flag[0] = false; 

/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
    flag[1] = false;
    /*delay */;
    flag[1] = true;
}
/* critical section*/;
flag[1] = false;

[... ] พิจารณาลำดับเหตุการณ์ต่อไปนี้:

  • P0 ตั้งค่าสถานะ [0] เป็นจริง
  • P1 ตั้งค่าสถานะ [1] เป็นจริง
  • P0 ตรวจสอบสถานะ [1]
  • P1 ตรวจสอบสถานะ [0]
  • P0 ตั้งค่าสถานะ [0] เป็นเท็จ
  • P1 ตั้งค่าสถานะ [1] เป็นเท็จ
  • P0 ตั้งค่าสถานะ [0] เป็นจริง
  • P1 ตั้งค่าสถานะ [1] เป็นจริง

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

ไม่ใช่เนื้อหาจากหนังสืออีกต่อไป

แล้ว Spinlocks ล่ะ?

Spinlock เป็นเทคนิคในการหลีกเลี่ยงค่าใช้จ่ายของกลไกการล็อค OS โดยทั่วไปแล้วคุณจะทำ:

try
{
   lock = beginLock();
   doSomething();
}
finally
{
   endLock();
}

ปัญหาเริ่มที่จะปรากฏขึ้นเมื่อค่าใช้จ่ายมากขึ้นกว่าbeginLock() doSomething()ในแง่ที่ตื่นเต้นมากลองจินตนาการว่าจะเกิดอะไรขึ้นเมื่อbeginLockราคา 1 วินาที แต่doSomethingราคาเพียง 1 มิลลิวินาที

ในกรณีนี้หากคุณรอ 1 มิลลิวินาทีคุณจะหลีกเลี่ยงการถูกขัดขวางเป็นเวลา 1 วินาที

ทำไมถึงbeginLockต้องเสียค่าใช้จ่ายมากมาย? หากการล็อคฟรีไม่เสียค่าใช้จ่ายมากนัก (ดูhttps://stackoverflow.com/a/49712993/5397116 ) แต่ถ้าการล็อคไม่ฟรีระบบปฏิบัติการจะ "หยุด" เธรดของคุณตั้งค่ากลไกเพื่อปลุกคุณ เมื่อล็อคเป็นอิสระแล้วปลุกคุณอีกครั้งในอนาคต

ทั้งหมดนี้มีราคาแพงกว่าลูปบางอันที่ตรวจสอบล็อค นั่นคือเหตุผลที่บางครั้งดีกว่าที่จะทำ "spinlock"

ตัวอย่างเช่น:

void beginSpinLock(lock)
{
   if(lock) loopFor(1 milliseconds);
   else 
   {
     lock = true;
     return;
   }

   if(lock) loopFor(2 milliseconds);
   else 
   {
     lock = true;
     return;
   }

   // important is that the part above never 
   // cause the thread to sleep.
   // It is "burning" the time slice of this thread.
   // Hopefully for good.

   // some implementations fallback to OS lock mechanism
   // after a few tries
   if(lock) return beginLock(lock);
   else 
   {
     lock = true;
     return;
   }
}

หากการนำไปปฏิบัติของคุณไม่ระวังคุณสามารถตกหล่นไปมาได้โดยใช้ CPU ทั้งหมดในกลไกการล็อค

ดูเพิ่มเติมที่:

https://preshing.com/20120226/roll-your-own-lightweight-mutex/
การใช้งานล็อคการหมุนของฉันถูกต้องและเหมาะสมหรือไม่

สรุป :

การหยุดชะงัก : สถานการณ์ที่ไม่มีใครคืบหน้าไม่ทำอะไรเลย (กำลังหลับรอ ฯลฯ ) การใช้ CPU จะต่ำ

Livelock : สถานการณ์ที่ไม่มีใครคืบหน้า แต่ใช้ CPU กับกลไกการล็อคไม่ใช่การคำนวณของคุณ

ความอดอยาก: สถานการณ์ที่ผู้ให้คำปรึกษาคนใดคนหนึ่งไม่เคยได้รับโอกาสเรียกใช้ โดยโชคร้ายบริสุทธิ์หรือโดยบางส่วนของคุณสมบัติของมัน (เช่นลำดับความสำคัญต่ำ);

Spinlock : เทคนิคการหลีกเลี่ยงค่าใช้จ่ายเพื่อรอการปลดล็อคเพื่อให้ได้อิสระ


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

ตัวอย่างมีน้อยมากที่เปิดโอกาสให้ตีความนี้ดังนั้นฉันจึงปรับปรุงมันให้ชัดเจนยิ่งขึ้นเกี่ยวกับความแตกต่างของพวกเขา หวังว่าจะช่วย
Daniel Frederico Lins Leite

ขอบคุณสำหรับการเพิ่มเกี่ยวกับ spinlocks ตามที่คุณ spinlocks เป็นเทคนิคและคุณเป็นธรรมเช่นกันและฉันเข้าใจ แต่สิ่งที่เกี่ยวกับปัญหาความผกผันของลำดับความสำคัญนั้นเมื่อกระบวนการหนึ่ง P1 อยู่ในส่วนที่สำคัญและกระบวนการลำดับความสำคัญสูงอื่น ๆ P2 ได้รับการกำหนดเวลาไว้ล่วงหน้า P1 ดังนั้นในกรณีนี้ CPU คือ P2 และกลไกการซิงโครไนซ์ของเรากับ P1 สิ่งนี้เรียกว่า Spinlock เนื่องจาก P1 อยู่ในสถานะพร้อมและ P2 อยู่ในสถานะทำงาน ที่นี่ spinlock เป็นปัญหา ฉันได้รับสิ่งที่ถูกต้องหรือไม่ ฉันไม่สามารถรับความซับซ้อนได้ถูกต้อง กรุณาช่วย
Vinay Yadav

คำแนะนำของฉันสำหรับคุณคือการสร้างคำถามอื่นที่ระบุปัญหาของคุณให้ชัดเจนยิ่งขึ้น ตอนนี้ถ้าคุณอยู่ใน "พื้นที่ผู้ใช้" และ P1 อยู่ในเซสชันที่สำคัญที่ได้รับการป้องกันด้วย SpinLock ที่มีลูปไม่สิ้นสุดและถูกจองไว้ก่อนหน้า จากนั้น P2 จะพยายามป้อนมันจะล้มเหลวและจะเผาเวลาทั้งหมด คุณสร้าง livelock (CPU หนึ่งตัวจะอยู่ที่ 100%) (การใช้งานที่ไม่ดีคือการปกป้องการซิงค์ IO ด้วย spinlock คุณสามารถลองตัวอย่างนี้ได้อย่างง่ายดาย) ใน "พื้นที่เคอร์เนล" บางทีข้อความนี้สามารถช่วยคุณได้: lxr.linux.no/linux+v3.6.6/ เอกสาร / ......
Daniel Frederico Lins Leite

ขอบคุณมากสำหรับคำชี้แจง อย่างไรก็ตามคำตอบของคุณค่อนข้างชัดเจนและมีประโยชน์ซึ่งแตกต่างจากคนอื่น ๆ
Vinay Yadav

13

DEADLOCK Deadlock เป็นเงื่อนไขที่ภารกิจจะรอเงื่อนไขที่ไม่สามารถทำได้ - งานอ้างสิทธิ์ควบคุมทรัพยากรที่ใช้ร่วมกันแบบเอกสิทธิ์ - งานเก็บทรัพยากรในขณะที่รอทรัพยากรอื่น ๆ ที่จะออก - งานไม่สามารถบังคับให้แยกแยะทรัพยากร - การรอแบบวงกลม สภาพมีอยู่

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


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

8

ตัวอย่างสองตัวอย่างนี้อาจแสดงให้คุณเห็นถึงความแตกต่างระหว่างการหยุดชะงักและการเคลื่อนไหว:


ตัวอย่าง Java สำหรับการหยุดชะงัก:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadlockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
        Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
        lock1.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 1");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } finally {
            lock1.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
        }
    }

    public static void doB() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
        lock2.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 2");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
            lock1.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } finally {
            lock2.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
        }
    }
}

ตัวอย่างผลลัพธ์:

Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2

ตัวอย่าง Java สำหรับ livelock:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LivelockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(LivelockSample::doA, "Thread A");
        Thread threadB = new Thread(LivelockSample::doB, "Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        try {
            while (!lock1.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                while (!lock2.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 2");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
                } finally {
                    lock2.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
                }
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }

    public static void doB() {
        try {
            while (!lock2.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                while (!lock1.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 1");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
                } finally {
                    lock1.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
                }
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }
}

ตัวอย่างผลลัพธ์:

Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...

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


รหัสเป็นสิ่งที่ดี แต่ตัวอย่างการล็อคสดไม่ดี ไม่ว่าเธรดจะถูกบล็อกในค่าหรือโพลสำหรับการเปลี่ยนแปลงค่าจะไม่แตกต่างกันตามแนวคิด การเปลี่ยนแปลงอย่างง่ายเพื่อแสดงให้เห็นถึงการล็อคสดคือการให้เธรด A และ B ปลดล็อคที่พวกเขามีเมื่อพวกเขารู้ว่าพวกเขาไม่สามารถรับการล็อกที่สองที่พวกเขาต้องการ จากนั้นพวกเขานอนหลับเป็นวินาทีละครั้งขอล็อคที่พวกเขามีอยู่เดิมจากนั้นนอนอีกวินาทีและพยายามที่จะได้รับล็อคอีกครั้ง ดังนั้นวงจรสำหรับแต่ละอันคือ: 1) การได้รับของฉัน, 2) การนอนหลับ, 3) พยายามที่จะได้รับอื่น ๆ & ล้มเหลว, 4) การปล่อยของฉัน, 5) การนอนหลับ, 6) ทำซ้ำ
CognizantApe

1
ฉันสงสัยว่าการล็อคสดที่คุณคิดว่ามีอยู่จริงนานพอที่จะทำให้เกิดปัญหาหรือไม่ เมื่อคุณยกเลิกการล็อกทั้งหมดที่คุณถืออยู่เสมอเมื่อคุณไม่สามารถจัดสรรการล็อกครั้งถัดไปเงื่อนไขการหยุดชะงัก (และการล็อคสด) "การค้างและรอ" หายไปเพราะจริง ๆ แล้วไม่ต้องรออีกต่อไป ( en.wikipedia.org/wiki/Deadlock )
mmirwaldt

แน่นอนว่าเงื่อนไขการล็อคที่หายไปหายไปเพราะสิ่งเหล่านี้เป็นล็อคแบบสดที่เรากำลังพูดถึง ตัวอย่างที่ฉันให้นั้นคล้ายกับตัวอย่างห้องโถงมาตรฐานที่กำหนด: geeksforgeeks.org/deadlock-starvation-and-livelock , en.wikibooks.org/wiki/Operating_System_Design/Concurrency/ ......
CognizantApe

0

ลองนึกภาพคุณได้เธรด A และเธรด B พวกเขาทั้งคู่synchronisedอยู่บนวัตถุเดียวกันและในบล็อกนี้มีตัวแปรทั่วโลกที่พวกเขากำลังอัปเดต

static boolean commonVar = false;
Object lock = new Object;

...

void threadAMethod(){
    ...
    while(commonVar == false){
         synchornized(lock){
              ...
              commonVar = true
         }
    }
}

void threadBMethod(){
    ...
    while(commonVar == true){
         synchornized(lock){
              ...
              commonVar = false
         }
    }
}

ดังนั้นเมื่อด้ายเข้ามาในwhileวงและถือล็อคสิ่งที่มันไม่ได้จะทำอย่างไรและการตั้งค่าไปcommonVar trueแล้วด้าย B มาในเข้ามาในwhileวงและตั้งแต่commonVarเป็นtrueตอนนี้ก็คือสามารถที่จะถือล็อค มันไม่เป็นเช่นนั้นดำเนินการsynchronisedบล็อกและชุดกลับไปcommonVar falseตอนนี้เธรด A อีกครั้งจะได้รับเป็นหน้าต่าง CPU ใหม่มันกำลังจะออกจากwhileลูป แต่เธรด B เพิ่งตั้งค่ากลับเป็นfalseดังนั้นรอบจะวนซ้ำอีกครั้ง กระทู้ทำอะไรบางอย่าง (ดังนั้นพวกเขาจะไม่ถูกปิดกั้นในความหมายดั้งเดิม) แต่สำหรับอะไรที่สวยมาก

นอกจากนี้ยังอาจเป็นการดีที่จะกล่าวถึง livelock ที่ไม่จำเป็นต้องปรากฏที่นี่ ฉันสมมติว่าตัวจัดตารางเวลาสนับสนุนเธรดอื่นเมื่อsynchronisedบล็อกเสร็จสิ้นการดำเนินการ เวลาส่วนใหญ่ฉันคิดว่ามันเป็นความคาดหวังที่ยากจะตีและขึ้นอยู่กับหลายสิ่งที่เกิดขึ้นภายใต้ประทุน


ตัวอย่างที่ดี นอกจากนี้ยังแสดงให้เห็นว่าทำไมคุณควรอ่านและเขียนอะตอมในบริบทที่เกิดขึ้นพร้อมกันเสมอ หากลูป while อยู่ภายในบล็อคการซิงโครไนซ์ข้อมูลด้านบนจะไม่เป็นปัญหา
CognizantApe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.