"การแข่งขันข้อมูล" และ "สภาพการแข่งขัน" เป็นสิ่งเดียวกันในบริบทของการเขียนโปรแกรมพร้อมกัน


คำตอบ:


143

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

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

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

พิจารณาตัวอย่างง่ายๆต่อไปนี้โดยที่ x เป็นตัวแปรที่ใช้ร่วมกัน:

Thread 1    Thread 2

 lock(l)     lock(l)
 x=1         x=2
 unlock(l)   unlock(l)

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

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

การตรวจจับสภาพการแข่งขันมีประโยชน์มากกว่าการแข่งขันข้อมูล อย่างไรก็ตามนี่เป็นเรื่องยากมากที่จะบรรลุ

การสร้างตัวอย่างย้อนกลับก็เป็นเรื่องเล็กน้อยเช่นกัน นี้บล็อกโพสต์ยังได้อธิบายถึงความแตกต่างได้เป็นอย่างดีกับตัวอย่างการทำธุรกรรมธนาคารที่เรียบง่าย


1
"data race (... ) ไม่มีการซิงโครไนซ์ที่บังคับลำดับใด ๆ ระหว่างการเข้าถึงเหล่านี้" ฉันสับสนนิดหน่อย ในตัวอย่างของคุณการดำเนินการอาจเกิดขึ้นในทั้งสองคำสั่ง (= 1 แล้ว = 2 หรืออีกทางหนึ่ง) เหตุใดจึงไม่ใช่การแข่งขันข้อมูล
josinalvo

6
@josinalvo: มันเป็นสิ่งประดิษฐ์ของคำจำกัดความทางเทคนิคของการแข่งขันข้อมูลประเด็นสำคัญคือระหว่างการเข้าถึงทั้งสองจะมีการปลดล็อกและการได้รับการล็อก (สำหรับคำสั่งใดคำสั่งหนึ่งที่เป็นไปได้) ตามความหมายการปลดล็อกและการได้มาของการล็อกจะสร้างลำดับระหว่างการเข้าถึงทั้งสองดังนั้นจึงไม่มีการแย่งชิงข้อมูล
Baris Kasikci

การประสานข้อมูลไม่เคยเอกสารใด ๆโดยเฉพาะอย่างยิ่งการสั่งซื้อระหว่างปฏิบัติการดังนั้นนี้ไม่ได้เป็นวิธีที่โชคดีมากที่จะแสดงมันออกมา ในทางกลับกัน JMM ระบุว่าสำหรับการดำเนินการอ่านแต่ละครั้งจะต้องมีการตรวจสอบการเขียนที่แน่นอนแม้ในการแข่งขันข้อมูล เป็นการยากที่จะหลีกเลี่ยงการกล่าวถึงลำดับเหตุการณ์ที่เกิดขึ้นก่อนหน้าและการซิงค์อย่างชัดเจนแต่แม้แต่คำจำกัดความ JLS ก็ผิดในการกล่าวถึงสิ่งที่เกิดขึ้นก่อนหน้านี้ : ตามคำจำกัดความการเขียนแบบระเหยพร้อมกันสองรายการถือเป็นการแย่งชิงข้อมูล
Marko Topolnik

@BarisKasikci "สร้างคำสั่งซื้อ" ไม่มีความหมายที่แท้จริงเท่าที่ฉันกังวล เป็นเพียงคำพังพอน ฉันไม่เชื่อโดยสุจริตใจว่า "การแข่งขันข้อมูล" เป็นแนวคิดที่มีประโยชน์จากระยะไกลเนื่องจากตำแหน่งหน่วยความจำทุกตำแหน่งที่เข้าถึงได้โดยหลายเธรดถือได้ว่าอยู่ใน "การแข่งขันข้อมูล"
Noldorin

คู่ที่ได้รับการปลดปล่อยจะสร้างคำสั่งซื้อเสมอ คำอธิบายทั่วไปมีความยาว แต่ตัวอย่างที่ไม่สำคัญคือคู่สัญญาณรอ @Noldorin "สร้างคำสั่งซื้อ" หมายถึงคำสั่งที่เกิดขึ้นก่อนซึ่งเป็นแนวคิดหลักของทฤษฎีการเกิดพร้อมกัน (ดูเอกสารสรุปของ Lamport เกี่ยวกับเหตุการณ์ที่เกิดขึ้นก่อนความสัมพันธ์) และระบบการกระจาย การแข่งขันข้อมูลเป็นแนวคิดที่มีประโยชน์เนื่องจากการมีอยู่ของพวกเขาก่อให้เกิดปัญหามากมาย (เช่นความหมายที่ไม่ได้กำหนดตามโมเดลหน่วยความจำ C ++ ความหมายที่ซับซ้อนมากใน Java เป็นต้น) การตรวจจับและกำจัดของพวกเขาถือเป็นวรรณกรรมมากมายในการวิจัยและการปฏิบัติ
Baris Kasikci

20

ตามวิกิพีเดียคำว่า "สภาพการแข่งขัน" ถูกใช้มาตั้งแต่สมัยของประตูลอจิกอิเล็กทรอนิกส์ครั้งแรก ในบริบทของ Java เงื่อนไขการแย่งชิงสามารถเกี่ยวข้องกับทรัพยากรใด ๆ เช่นไฟล์การเชื่อมต่อเครือข่ายเธรดจากเธรดพูลเป็นต้น

คำว่า "การแข่งขันข้อมูล" ถูกสงวนไว้สำหรับการที่ดีที่สุดที่เฉพาะเจาะจงของความหมายที่กำหนดโดยJLS

กรณีที่น่าสนใจที่สุดคือสภาพการแข่งขันที่คล้ายกับการแข่งขันข้อมูลมาก แต่ก็ยังไม่เป็นเช่นนั้นเช่นในตัวอย่างง่ายๆนี้:

class Race {
  static volatile int i;
  static int uniqueInt() { return i++; }
}

เนื่องจากiมีความผันผวนจึงไม่มีการแย่งชิงข้อมูล แต่จากมุมมองโปรแกรมถูกต้องมีสภาพการแข่งขันเนื่องจากการที่ไม่ใช่ atomicity ของทั้งสองดำเนินการ: อ่านi, i+1เขียน uniqueIntหลายหัวข้ออาจได้รับค่าเดียวกันจาก


1
คุณสามารถวางบรรทัดในคำตอบของคุณที่อธิบายdata raceความหมายที่แท้จริงใน JLS ได้หรือไม่?
Geek

@geek คำว่า "JLS" คือไฮเปอร์ลิงก์ไปยังส่วนที่เกี่ยวข้องของ JLS
Marko Topolnik

@MarkoTopolnik ฉันสับสนกับตัวอย่างเล็กน้อย คุณช่วยอธิบายได้ไหม: "เนื่องจากฉันมีความผันผวนจึงไม่มีการแย่งชิงข้อมูล" Voltility ทำให้มั่นใจได้ว่าสามารถมองเห็นได้ แต่ยังคง: 1) ไม่ซิงโครไนซ์และหลายเธรดสามารถอ่าน / เขียนได้ในเวลาเดียวกันและ 2) เป็นฟิลด์ที่ไม่ใช้ร่วมกันที่ใช้ร่วมกันดังนั้นตาม Java Concurrency in Practice (อ้างถึงด้านล่างด้วย) มันคือ data race ไม่ใช่ race condition ใช่หรือไม่?
aniliitb10

@ aniliitb10 แทนที่จะใช้คำพูดมือสองที่ฉีกขาดจากบริบทของพวกเขาคุณควรทบทวน JLS ส่วน 17.4 ที่ฉันเชื่อมโยงไว้ในคำตอบของฉัน การเข้าถึงตัวแปรระเหยเป็นการดำเนินการซิงโครไนซ์ตามที่กำหนดไว้ใน§17.4.2
Marko Topolnik

@ aniliitb10 Votaltiles ไม่ก่อให้เกิดการแย่งชิงข้อมูลเนื่องจากสามารถสั่งการเข้าถึงได้ นั่นคือคุณสามารถให้เหตุผลคำสั่งของพวกเขาด้วยวิธีนี้หรือวิธีนั้นซึ่งนำไปสู่ผลลัพธ์ที่แตกต่างกัน ด้วยการแข่งขันด้านข้อมูลคุณไม่มีทางให้เหตุผลในการสั่งซื้อได้ ตัวอย่างเช่นการดำเนินการ i ++ ของแต่ละเธรดอาจเกิดขึ้นตามค่าที่แคชไว้ในเครื่อง i คุณไม่มีทางสั่งการดำเนินการเหล่านั้นได้ทั่วโลก (จากมุมมองของโปรแกรมเมอร์) - เว้นแต่คุณจะมีโมเดลหน่วยความจำภาษาที่แน่นอน
Xiao-Feng Li

3

ไม่พวกเขาแตกต่างกันและทั้งสองไม่ได้เป็นส่วนย่อยของหนึ่งหรือในทางกลับกัน

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

นำมาจากหนังสือยอดเยี่ยม - Java Concurrency in Practice โดย Joshua Bloch & Co.


โปรดทราบว่าคำถามมีแท็กที่ไม่เข้าใจภาษา
martinkunev

1

TL; DR: ความแตกต่างระหว่างการแข่งขันของข้อมูลและสภาพการแข่งขันขึ้นอยู่กับลักษณะของการกำหนดปัญหาและตำแหน่งที่จะกำหนดขอบเขตระหว่างพฤติกรรมที่ไม่ได้กำหนดและพฤติกรรมที่กำหนดไว้อย่างชัดเจน แต่ไม่แน่นอน ความแตกต่างในปัจจุบันเป็นแบบธรรมดาและสะท้อนให้เห็นถึงส่วนติดต่อระหว่างสถาปนิกโปรเซสเซอร์และภาษาโปรแกรมได้ดีที่สุด

1. อรรถศาสตร์

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

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

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

2. ทำไมความแตกต่าง?

แต่ถ้าคำสั่งซื้อยังคงไม่แน่นอนในสภาพการแข่งขันทำไมต้องแยกความแตกต่างจากการแข่งขันข้อมูล เหตุผลอยู่ในทางปฏิบัติมากกว่าทางทฤษฎี เป็นเพราะความแตกต่างมีอยู่ในส่วนต่อประสานระหว่างภาษาโปรแกรมและสถาปัตยกรรมตัวประมวลผล

คำแนะนำในการโหลด / จัดเก็บหน่วยความจำในสถาปัตยกรรมสมัยใหม่มักใช้เป็นการเข้าถึงหน่วยความจำแบบ "บริสุทธิ์" เนื่องจากลักษณะของไปป์ไลน์ที่ไม่ได้รับคำสั่งการเก็งกำไรแคชหลายระดับการเชื่อมต่อโครงข่าย cpu-ram โดยเฉพาะแบบมัลติคอร์เป็นต้น มีหลายปัจจัยที่นำไปสู่การกำหนดเวลาและการสั่งซื้อที่ไม่แน่นอน ในการบังคับใช้คำสั่งสำหรับทุกคำสั่งหน่วยความจำจะต้องรับโทษอย่างมากโดยเฉพาะอย่างยิ่งในการออกแบบโปรเซสเซอร์ที่รองรับมัลติคอร์ ดังนั้นความหมายการสั่งซื้อจึงมีคำแนะนำเพิ่มเติมเช่นอุปสรรคต่างๆ (หรือรั้ว)

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

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

3. โมเดลหน่วยความจำภาษา

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

ที่กล่าวว่าหากภาษาไม่ต้องการเปิดเผยพฤติกรรมระดับต่ำของโปรเซสเซอร์ (และยินดีที่จะสละประโยชน์ด้านประสิทธิภาพบางประการของสถาปัตยกรรมสมัยใหม่) พวกเขาสามารถเลือกกำหนดรูปแบบหน่วยความจำที่ซ่อนรายละเอียดของ "บริสุทธิ์" ได้อย่างสมบูรณ์ การเข้าถึงหน่วยความจำ แต่ใช้ความหมายการสั่งซื้อสำหรับการดำเนินการหน่วยความจำทั้งหมด จากนั้นผู้พัฒนาคอมไพลเลอร์ / รันไทม์อาจเลือกที่จะปฏิบัติต่อตัวแปรหน่วยความจำทุกตัวแปรแบบระเหยในสถาปัตยกรรมโปรเซสเซอร์ สำหรับภาษาเหล่านี้ (ที่รองรับหน่วยความจำแบบแบ่งใช้ข้ามเธรด) ไม่มีการแข่งขันของข้อมูล แต่อาจยังคงเป็นเงื่อนไขการแข่งขันแม้จะมีภาษาที่สอดคล้องกันอย่างสมบูรณ์

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

4. สรุป

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

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