เมื่อเขียนแอพพลิเคชั่นแบบมัลติเธรดหนึ่งในปัญหาที่พบบ่อยที่สุดคือการหยุดชะงัก
คำถามของฉันต่อชุมชนคือ:
การหยุดชะงักคืออะไร
คุณตรวจจับพวกมันได้อย่างไร
คุณจัดการกับพวกเขา?
และในที่สุดคุณจะป้องกันไม่ให้เกิดขึ้นได้อย่างไร
เมื่อเขียนแอพพลิเคชั่นแบบมัลติเธรดหนึ่งในปัญหาที่พบบ่อยที่สุดคือการหยุดชะงัก
คำถามของฉันต่อชุมชนคือ:
การหยุดชะงักคืออะไร
คุณตรวจจับพวกมันได้อย่างไร
คุณจัดการกับพวกเขา?
และในที่สุดคุณจะป้องกันไม่ให้เกิดขึ้นได้อย่างไร
คำตอบ:
การล็อคเกิดขึ้นเมื่อหลายกระบวนการพยายามเข้าถึงทรัพยากรเดียวกันในเวลาเดียวกัน
กระบวนการหนึ่งสูญเสียและต้องรอให้กระบวนการอื่นเสร็จสิ้น
การหยุดชะงักเกิดขึ้นเมื่อกระบวนการรอยังคงยึดทรัพยากรอื่นที่ความต้องการครั้งแรกก่อนที่จะเสร็จสิ้น
ตัวอย่างเช่น:
ทรัพยากร A และทรัพยากร B ถูกใช้โดยกระบวนการ X และกระบวนการ Y
วิธีที่ดีที่สุดในการหลีกเลี่ยงการหยุดชะงักคือการหลีกเลี่ยงการข้ามกระบวนการด้วยวิธีนี้ ลดความจำเป็นในการล็อคทุกอย่างให้มากที่สุด
ในฐานข้อมูลหลีกเลี่ยงการเปลี่ยนแปลงตารางที่แตกต่างกันจำนวนมากในการทำธุรกรรมครั้งเดียวหลีกเลี่ยงทริกเกอร์และเปลี่ยนไปอ่านในแง่ดี / สกปรก / nolock มากที่สุดเท่าที่จะทำได้
ให้ฉันอธิบายโลกแห่งความจริง (ไม่ใช่จริง) ตัวอย่างสำหรับสถานการณ์การหยุดชะงักจากภาพยนตร์อาชญากรรม ลองนึกภาพคนร้ายจับตัวประกันและต่อต้านตำรวจจับตัวประกันซึ่งเป็นเพื่อนของอาชญากร ในกรณีนี้อาชญากรจะไม่ปล่อยให้ตัวประกันไปหากตำรวจไม่ยอมให้เพื่อนของเขาปล่อยมือ ตำรวจก็จะไม่ปล่อยเพื่อนคนร้ายไปด้วยเว้นแต่อาชญากรจะปล่อยตัวประกันออกไป นี่เป็นสถานการณ์ที่ไม่น่าเชื่อถืออย่างไม่มีที่สิ้นสุดเนื่องจากทั้งสองฝ่ายยืนยันขั้นตอนแรกจากกันและกัน
ดังนั้นเมื่อสองเธรดต้องการทรัพยากรที่แตกต่างกันสองรายการและแต่ละรายการมีการล็อกของทรัพยากรที่ความต้องการอื่น ๆ ก็คือการหยุดชะงัก
คุณกำลังคบกับผู้หญิงและวันหนึ่งหลังจากที่ทะเลาะกันทั้งสองฝ่ายมีหัวใจเสียกับแต่ละอื่น ๆ และรอI-AM-เสียใจและฉันพลาดคุณเรียก ในสถานการณ์เช่นนี้ทั้งสองฝ่ายต้องการสื่อสารซึ่งกันและกันถ้าหากหนึ่งในนั้นได้รับสายI-am- ขอโทษจากอีกฝ่ายหนึ่ง เพราะสิ่งนั้นไม่ได้เริ่มที่จะสื่อสารกันและรออยู่ในสถานะไม่โต้ตอบทั้งคู่จะรอให้อีกฝ่ายเริ่มการสื่อสารซึ่งจบลงด้วยสถานการณ์ที่หยุดชะงัก
การหยุดชะงักจะเกิดขึ้นเฉพาะเมื่อคุณมีการล็อคสองตัวขึ้นไปที่สามารถใช้งานได้ในเวลาเดียวกันและจะถูกล็อคตามลำดับที่แตกต่างกัน
วิธีในการหลีกเลี่ยงการหยุดชะงักคือ:
เพื่อกำหนด deadlock ก่อนอื่นฉันจะกำหนดกระบวนการ
กระบวนการ :ตามที่เรารู้ว่ากระบวนการนั้นไม่มีอะไรนอกจากการprogram
ดำเนินการ
ทรัพยากร :ในการดำเนินการกระบวนการโปรแกรมต้องการทรัพยากรบางอย่าง หมวดหมู่ทรัพยากรอาจรวมถึงหน่วยความจำเครื่องพิมพ์ซีพียูไฟล์ที่เปิดเทปไดรฟ์ซีดีรอม ฯลฯ
การหยุดชะงัก : การหยุดชะงักเป็นสถานการณ์หรือเงื่อนไขเมื่อกระบวนการสองกระบวนการขึ้นไปถือทรัพยากรบางอย่างและพยายามรับทรัพยากรเพิ่มเติมและพวกเขาไม่สามารถปล่อยทรัพยากรได้จนกว่าจะเสร็จสิ้นการดำเนินการที่นั่น
สภาพหรือสถานการณ์การหยุดชะงัก
ในแผนภาพข้างต้นมีสองขั้นตอนP1และP2และมีสองแหล่งR1และR2
ทรัพยากรR1จะถูกจัดสรรในการประมวลผลP1และทรัพยากรR2จะจัดสรรให้กระบวนการp2 ในการดำเนินการตามกระบวนการP1ให้สมบูรณ์ต้องใช้ทรัพยากรR2ดังนั้นการร้องขอP1สำหรับR2แต่R2ได้รับการจัดสรรให้กับP2แล้ว
ในทำนองเดียวกันการดำเนินการP2เพื่อให้การประมวลผลสมบูรณ์ต้องใช้R1แต่R1ได้รับการจัดสรรให้กับP1แล้ว
ทั้งกระบวนการไม่สามารถปล่อยทรัพยากรของพวกเขาจนกว่าและจนกว่าพวกเขาจะเสร็จสิ้นการดำเนินการของพวกเขา ดังนั้นทั้งสองกำลังรอทรัพยากรอื่นและพวกเขาจะรอตลอดไป ดังนั้นนี่คือเงื่อนไขDEADLOCK
เพื่อให้การหยุดชะงักเกิดขึ้นต้องมีสี่เงื่อนไข
และเงื่อนไขเหล่านี้ทั้งหมดมีความพึงพอใจในแผนภาพข้างต้น
การหยุดชะงักเกิดขึ้นเมื่อเธรดกำลังรอบางสิ่งที่ไม่เคยเกิดขึ้น
โดยทั่วไปแล้วจะเกิดขึ้นเมื่อเธรดกำลังรอ mutex หรือเซมาฟอร์ที่ไม่เคยปล่อยโดยเจ้าของคนก่อน
มันมักจะเกิดขึ้นเมื่อคุณมีสถานการณ์ที่เกี่ยวข้องกับสองเธรดและการล็อกสองแบบดังนี้:
Thread 1 Thread 2
Lock1->Lock(); Lock2->Lock();
WaitForLock2(); WaitForLock1(); <-- Oops!
โดยทั่วไปคุณตรวจพบสิ่งเหล่านี้เพราะสิ่งที่คุณคาดหวังว่าจะไม่เกิดขึ้นหรือแอปพลิเคชันหยุดทำงานโดยสิ้นเชิง
คุณสามารถใช้ดูที่นี้บทความที่ยอดเยี่ยมภายใต้ส่วนหยุดชะงัก มันอยู่ใน C # แต่ความคิดยังคงเหมือนเดิมสำหรับแพลตฟอร์มอื่น ฉันพูดที่นี่เพื่ออ่านง่าย
การหยุดชะงักเกิดขึ้นเมื่อสองเธรดแต่ละครั้งรอทรัพยากรที่ถือโดยอีกดังนั้นจึงไม่สามารถดำเนินการต่อ วิธีที่ง่ายที่สุดในการอธิบายสิ่งนี้คือการล็อคสองอัน:
object locker1 = new object();
object locker2 = new object();
new Thread (() => {
lock (locker1)
{
Thread.Sleep (1000);
lock (locker2); // Deadlock
}
}).Start();
lock (locker2)
{
Thread.Sleep (1000);
lock (locker1); // Deadlock
}
การหยุดชะงักเป็นปัญหาที่พบบ่อยในการแก้ปัญหามัลติโปรเซสเซอร์ / มัลติโปรแกรมในระบบปฏิบัติการ สมมติว่ามีสองกระบวนการ P1, P2 และทรัพยากรร่วมกันทั่วโลกสองแห่ง R1, R2 และในส่วนวิกฤตทรัพยากรทั้งสองต้องเข้าถึงได้
เริ่มแรกระบบปฏิบัติการจะกำหนด R1 ให้ดำเนินการ P1 และ R2 เพื่อประมวลผล P2 ในขณะที่กระบวนการทั้งสองกำลังทำงานพร้อมกันพวกเขาอาจเริ่มดำเนินการรหัสของพวกเขา แต่ปัญหาเกิดขึ้นเมื่อกระบวนการเข้าชมส่วนที่สำคัญ ดังนั้นกระบวนการ R1 จะรอให้โพรเซส P2 ปล่อย R2 และในทางกลับกัน ... ดังนั้นพวกเขาจะรอตลอดไป (DEADLOCK CONDITION)
อะนาล็อกขนาดเล็ก ...
คุณแม่ (OS),
คุณ (P1),
พี่ชายของคุณ (P2),
Apple (R1), มี
ด (R2),
ส่วนที่สำคัญ (การตัดแอปเปิ้ลด้วยมีด)แม่ของคุณให้แอปเปิ้ลและมีดแก่คุณในตอนแรก
ทั้งสองมีความสุขและเล่น (ใช้รหัส)
ทุกคนที่คุณต้องการตัดแอปเปิ้ล (ส่วนที่สำคัญ) ในบางจุด
คุณไม่ต้องการให้แอปเปิ้ลกับพี่ชายของคุณ
พี่ชายของคุณไม่ต้องการให้มีดกับคุณ
ดังนั้นคุณทั้งคู่จะต้องรอกันนานมาก :)
การหยุดชะงักเกิดขึ้นเมื่อมีการล็อคสองเธรดเพื่อป้องกันไม่ให้มีการดำเนินการใด ๆ วิธีที่ดีที่สุดในการหลีกเลี่ยงพวกมันคือการพัฒนาอย่างระมัดระวัง ระบบฝังตัวจำนวนมากปกป้องพวกเขาโดยใช้ตัวจับเวลาจ้องจับผิด (ตัวจับเวลาที่รีเซ็ตระบบเมื่อใดก็ตามที่มันแฮงค์ในช่วงระยะเวลาหนึ่ง)
การหยุดชะงักเกิดขึ้นเมื่อมีห่วงโซ่วงกลมของเธรดหรือกระบวนการที่แต่ละคนมีทรัพยากรที่ถูกล็อคและพยายามที่จะล็อคทรัพยากรที่มีองค์ประกอบต่อไปในห่วงโซ่ ตัวอย่างเช่นสองเธรดที่เก็บล็อค A และล็อค B ตามลำดับและทั้งคู่พยายามรับล็อคอื่น
โปรแกรมคลาสสิกและเรียบง่ายมากสำหรับการเข้าใจสถานการณ์การหยุดชะงัก : -
public class Lazy {
private static boolean initialized = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
initialized = true;
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}
เมื่อเธรดหลักเรียกใช้ Lazy.main จะตรวจสอบว่ามีการเริ่มต้นคลาส Lazy และเริ่มต้นคลาสหรือไม่ ตอนนี้เธรดหลักตั้งค่าการเตรียมใช้งานเป็นเท็จสร้างและเริ่มเธรดพื้นหลังซึ่งมีวิธีการตั้งค่าเริ่มต้นเป็นจริงและรอให้เธรดพื้นหลังเสร็จสมบูรณ์
เวลานี้คลาสกำลังเริ่มต้นโดยเธรดอื่น ภายใต้สถานการณ์เหล่านี้เธรดปัจจุบันซึ่งเป็นเธรดพื้นหลังรอวัตถุคลาสจนกว่าการเริ่มต้นจะเสร็จสมบูรณ์ น่าเสียดายที่เธรดที่กำลังเตรียมใช้งานคือเธรดหลักกำลังรอให้เธรดพื้นหลังดำเนินการจนเสร็จสิ้น เนื่องจากเธรดทั้งสองกำลังรอซึ่งกันและกันโปรแกรมจึงถูกDEADLOCKED
การหยุดชะงักคือสถานะของระบบที่ไม่มีกระบวนการ / เธรดเดียวที่สามารถดำเนินการกระทำได้ ดังที่ผู้อื่นกล่าวถึงการหยุดชะงักโดยทั่วไปแล้วเป็นผลมาจากสถานการณ์ที่แต่ละกระบวนการ / เธรดต้องการรับการล็อกไปยังทรัพยากรที่ถูกล็อกโดยกระบวนการ / เธรดอื่น (หรือแม้แต่เดียวกัน)
มีวิธีการต่างๆในการค้นหาและหลีกเลี่ยง หนึ่งคือคิดหนักมากและ / หรือลองสิ่งต่าง ๆ มากมาย อย่างไรก็ตามการจัดการกับความเท่าเทียมนั้นเป็นเรื่องยากและคนส่วนใหญ่ (ถ้าไม่ทั้งหมด) จะไม่สามารถหลีกเลี่ยงปัญหาได้อย่างสมบูรณ์
วิธีการที่เป็นทางการมากขึ้นอาจมีประโยชน์หากคุณจริงจังกับการจัดการกับปัญหาประเภทนี้ วิธีการที่เป็นประโยชน์มากที่สุดที่ฉันรู้คือการใช้วิธีการทางทฤษฎีกระบวนการ ที่นี่คุณจำลองระบบของคุณในภาษากระบวนการบางอย่าง (เช่น CCS, CSP, ACP, mCRL2, LOTOS) และใช้เครื่องมือที่มีอยู่เพื่อ (รุ่น -) ตรวจสอบการหยุดชะงัก (และคุณสมบัติอื่น ๆ เช่นกัน) ตัวอย่างชุดเครื่องมือที่ใช้คือ FDR, mCRL2, CADP และ Uppaal วิญญาณที่กล้าหาญบางคนอาจพิสูจน์ว่าระบบของพวกเขาปราศจากการหยุดชะงักโดยใช้วิธีการทางสัญลักษณ์ล้วนๆ (การพิสูจน์ทฤษฎีบท; มองหา Owicki-Gries)
อย่างไรก็ตามวิธีการแบบเป็นทางการเหล่านี้ต้องการความพยายามบางอย่าง (เช่นการเรียนรู้พื้นฐานของทฤษฎีกระบวนการ) แต่ฉันเดาว่าเป็นเพียงผลมาจากความจริงที่ว่าปัญหาเหล่านี้ยาก
การหยุดชะงักคือสถานการณ์ที่เกิดขึ้นเมื่อมีทรัพยากรที่มีอยู่น้อยลงตามที่ร้องขอโดยกระบวนการที่แตกต่างกัน หมายความว่าเมื่อจำนวนทรัพยากรที่มีอยู่น้อยกว่าที่ผู้ใช้ร้องขอในเวลานั้นกระบวนการจะอยู่ในสภาพรอคอยบางครั้งการรอเพิ่มมากขึ้นและไม่มีโอกาสที่จะตรวจสอบปัญหาการขาดแคลนทรัพยากรแล้ว สถานการณ์นี้เรียกว่าการหยุดชะงัก ที่จริงแล้วการหยุดชะงักเป็นปัญหาสำคัญสำหรับเราและมันเกิดขึ้นเฉพาะในระบบปฏิบัติการหลายภารกิจ. adadlock ไม่สามารถเกิดขึ้นได้ในระบบปฏิบัติการ tasking เดียวเพราะทรัพยากรทั้งหมดมีอยู่สำหรับงานที่กำลังทำงานอยู่ ......
คำอธิบายข้างต้นเป็นสิ่งที่ดี หวังว่านี่อาจเป็นประโยชน์: https://ora-data.blogspot.in/2017/04/deadlock-in-oracle.html
ในฐานข้อมูลเมื่อเซสชั่น (เช่น ora) ต้องการทรัพยากรที่ถือครองโดยเซสชั่นอื่น (เช่นข้อมูล) แต่เซสชั่น (ข้อมูล) นั้นยังต้องการทรัพยากรที่จัดขึ้นโดยเซสชั่นแรก (ora) อาจมีมากกว่า 2 เซสชันที่เกี่ยวข้อง แต่ความคิดจะเหมือนกัน ที่จริงแล้ว Deadlock ป้องกันการทำธุรกรรมบางอย่างจากการทำงาน ตัวอย่างเช่นสมมติว่า ORA-DATA เก็บล็อค A และขอล็อค B และ SKU เก็บล็อค B และขอล็อค A
ขอบคุณ
การหยุดชะงักเกิดขึ้นเมื่อเธรดกำลังรอเธรดอื่นให้เสร็จและในทางกลับกัน
จะหลีกเลี่ยงได้อย่างไร?
- หลีกเลี่ยงการล็อคที่ซ้อนกัน
- หลีกเลี่ยงการล็อคที่ไม่จำเป็น
- ใช้การรวมเธรด ()
คุณตรวจจับได้อย่างไร
รันคำสั่งนี้ใน cmd:
jcmd $PID Thread.print
อ้างอิง : geeksforgeeks
การหยุดชะงักไม่เพียงเกิดขึ้นกับการล็อกเท่านั้น แต่นั่นเป็นสาเหตุที่พบบ่อยที่สุด ใน C ++ คุณสามารถสร้าง deadlock ด้วยสองเธรดและไม่มีการล็อกโดยให้แต่ละการเรียก thread join join () บน std :: thread object สำหรับอีกอัน
การใช้การล็อคเพื่อควบคุมการเข้าถึงทรัพยากรที่ใช้ร่วมกันมีแนวโน้มที่จะเกิดการชะงักงันและตัวกำหนดเวลาการทำธุรกรรมเพียงอย่างเดียวไม่สามารถป้องกันการเกิดขึ้นได้
ยกตัวอย่างเช่นระบบฐานข้อมูลเชิงสัมพันธ์ใช้ล็อคต่างๆในการทำธุรกรรมการรับประกันคุณสมบัติ ACID
ไม่ว่าคุณจะใช้ระบบฐานข้อมูลเชิงสัมพันธ์ใดการล็อคจะได้รับเสมอเมื่อทำการแก้ไข (เช่นUPDATE
หรือDELETE
) บันทึกตารางบางอย่าง โดยไม่ต้องล็อคแถวที่ได้รับการแก้ไขโดยการทำธุรกรรมที่กำลังทำงานอยู่ที่อะตอมมิกซิตี้จะถูกทำลาย
ตามที่ฉันได้อธิบายไว้ในบทความนี้การหยุดชะงักเกิดขึ้นเมื่อธุรกรรมที่เกิดขึ้นพร้อมกันสองรายการไม่สามารถดำเนินการได้เนื่องจากแต่ละรายการรอให้อีกฝ่ายปล่อยการล็อกดังแสดงในแผนภาพต่อไปนี้
เนื่องจากธุรกรรมทั้งสองอยู่ในช่วงการล็อกการล็อกจึงไม่มีการปล่อยการล็อกก่อนที่จะรับการล็อกถัดไป
หากคุณกำลังใช้อัลกอริทึมการควบคุมการเกิดพร้อมกันที่ต้องอาศัยการล็อคดังนั้นจึงมีความเสี่ยงที่จะทำงานในสถานการณ์การหยุดชะงัก การหยุดชะงักสามารถเกิดขึ้นได้ในสภาพแวดล้อมที่เกิดขึ้นพร้อมกันไม่เพียง แต่ในระบบฐานข้อมูล
ตัวอย่างเช่นโปรแกรมมัลติเธรดสามารถหยุดชะงักได้หากเธรดตั้งแต่สองเธรดขึ้นไปกำลังรอการล็อกที่ได้รับมาก่อนหน้านี้ดังนั้นจึงไม่มีเธรดใดที่สามารถดำเนินการได้ หากสิ่งนี้เกิดขึ้นในแอ็พพลิเคชัน Java JVM ไม่สามารถบังคับให้เธรดหยุดการเรียกใช้และปล่อยการล็อก
แม้ว่าThread
คลาส exposes stop
เมธอดเมธอดนั้นถูกเลิกใช้ตั้งแต่ Java 1.1 เนื่องจากสามารถทำให้อ็อบเจ็กต์ถูกทิ้งให้อยู่ในสถานะไม่สอดคล้องกันหลังจากที่เธรดหยุดทำงาน Java กำหนดแทนinterrupt
วิธีการซึ่งทำหน้าที่เป็นคำใบ้ในขณะที่เธรดที่ถูกขัดจังหวะสามารถละเว้นการขัดจังหวะและดำเนินการตามปกติ
ด้วยเหตุผลนี้แอปพลิเคชัน Java ไม่สามารถกู้คืนจากสถานการณ์การหยุดชะงักและเป็นความรับผิดชอบของผู้พัฒนาแอปพลิเคชันในการสั่งซื้อคำขอการล็อกการล็อกในลักษณะที่การล็อกไม่สามารถเกิดขึ้นได้
อย่างไรก็ตามระบบฐานข้อมูลไม่สามารถบังคับใช้คำสั่งซื้อการล็อคที่กำหนดเนื่องจากเป็นไปไม่ได้ที่จะคาดการณ์ว่าการล็อคอื่น ๆ ของธุรกรรมบางรายการจะต้องการได้รับอะไรเพิ่มเติม การรักษาลำดับการล็อกให้เป็นความรับผิดชอบของ data access layer และฐานข้อมูลสามารถช่วยในการกู้คืนจากสถานการณ์การหยุดชะงักเท่านั้น
เอ็นจิ้นฐานข้อมูลรันกระบวนการแยกต่างหากที่สแกนกราฟข้อขัดแย้งในปัจจุบันเพื่อดูรอบการรอ (ซึ่งเกิดจากการหยุดชะงัก) เมื่อตรวจพบรอบเอ็นจิ้นฐานข้อมูลจะเลือกหนึ่งธุรกรรมและยกเลิกมันทำให้การล็อกถูกปล่อยเพื่อให้ธุรกรรมอื่นสามารถดำเนินการได้
แตกต่างจาก JVM ธุรกรรมฐานข้อมูลได้รับการออกแบบเป็นหน่วยการทำงานของอะตอม ดังนั้นการย้อนกลับจะทำให้ฐานข้อมูลอยู่ในสถานะที่สอดคล้องกัน
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้ลองอ่านบทความนี้ด้วย
Mutex เป็นสาระสำคัญเป็นล็อคให้การป้องกันการเข้าถึงทรัพยากรที่ใช้ร่วมกัน ภายใต้ Linux ชนิดข้อมูลเธรด mutex คือ pthread_mutex_t ก่อนใช้งานให้เตรียมข้อมูลเบื้องต้น
ในการเข้าถึงทรัพยากรที่ใช้ร่วมกันคุณต้องล็อค mutex หาก mutex อยู่ในล็อกการโทรจะบล็อกเธรดจนกว่า mutex จะถูกปลดล็อก เมื่อเสร็จสิ้นการเยี่ยมชมทรัพยากรที่ใช้ร่วมกันคุณจะต้องปลดล็อคพวกเขา
โดยรวมแล้วมีหลักการพื้นฐานที่ไม่ได้เขียนไว้สองสามข้อ:
รับการล็อคก่อนใช้ทรัพยากรที่ใช้ร่วมกัน
ล็อคกุญแจให้สั้นที่สุดเท่าที่จะทำได้
ปล่อยการล็อกถ้าเธรดส่งคืนข้อผิดพลาด