ฉันได้ฟังและอ่านบทความพูดคุยและคำถามเกี่ยวกับสแต็คโอเวอร์โฟลว์หลายเรื่องstd::atomic
แล้วและฉันต้องการให้แน่ใจว่าฉันเข้าใจดี เนื่องจากฉันยังสับสนอยู่เล็กน้อยกับบรรทัดแคชเขียนการมองเห็นเนื่องจากความล่าช้าที่เป็นไปได้ในโปรโตคอลการเชื่อมโยงกันของแคช MESI (หรือที่ได้รับ) บัฟเฟอร์ที่เก็บบัฟเฟอร์คิวที่ไม่ถูกต้องและอื่น ๆ
ฉันอ่าน x86 มีโมเดลหน่วยความจำที่แรงกว่าและหากการตรวจสอบความถูกต้องแคชล่าช้า x86 สามารถยกเลิกการดำเนินการที่เริ่มต้นได้ แต่ตอนนี้ฉันสนใจเฉพาะสิ่งที่ฉันควรถือว่าเป็นโปรแกรมเมอร์ C ++ ซึ่งเป็นอิสระจากแพลตฟอร์ม
[T1: thread1 T2: thread2 V1: ตัวแปรอะตอมมิกที่ใช้ร่วมกัน]
ฉันเข้าใจว่า std :: atomic รับประกันว่า
(1) ไม่มีการแย่งข้อมูลในตัวแปร (ต้องขอบคุณการเข้าถึงสายแคชเท่านั้น)
(2) หน่วยความจำที่เราใช้นั้นขึ้นอยู่กับหน่วยความจำที่รับประกันว่า (มีสิ่งกีดขวาง) ซึ่งความสอดคล้องตามลำดับจะเกิดขึ้น
(3) หลังจากการเขียนอะตอมมิก (V1) บน T1 อะตอมมิก RMW (V1) บน T2 จะสอดคล้องกัน (บรรทัดแคชจะได้รับการอัพเดตด้วยค่าที่เขียนบน T1)
แต่เมื่อไพรเมอร์เชื่อมโยงกันถึงแคช
ความหมายของสิ่งเหล่านี้คือโดยค่าเริ่มต้นการโหลดสามารถดึงข้อมูลเก่า (ถ้าคำขอการตรวจสอบความถูกต้องที่สอดคล้องกันกำลังนั่งอยู่ในคิวการตรวจสอบความถูกต้อง)
ดังนั้นถูกต้องหรือไม่
(4) std::atomic
ไม่รับประกันว่า T2 จะไม่อ่านค่า 'เก่า' ในการอ่านอะตอมมิก (V) หลังจากการเขียนอะตอม (V) บน T1
คำถามถ้า (4) ถูกต้อง: ถ้าการเขียนอะตอมของ T1 ทำให้บรรทัดแคชไม่ว่าจะล่าช้าหรือไม่ทำไม T2 จึงรอให้การทำให้มีผลใช้งานไม่ได้เมื่อการดำเนินการ RMW ของอะตอม แต่ไม่ได้อ่านในอะตอม?
คำถามที่ (4) ไม่ถูกต้อง: เมื่อใดที่เธรดสามารถอ่านค่า 'เก่า' และ "สามารถมองเห็นได้" ในการดำเนินการ
ฉันขอขอบคุณคำตอบของคุณมาก
อัปเดต 1
ดังนั้นดูเหมือนว่าฉันผิดในวันที่ (3) ลองจินตนาการถึง interleave ต่อไปนี้สำหรับ V1 = 0 เริ่มต้น:
T1: W(1)
T2: R(0) M(++) W(1)
แม้ว่า RMW ของ T2 จะรับประกันว่าจะเกิดขึ้นทั้งหมดหลังจาก W (1) ในกรณีนี้ แต่ก็ยังสามารถอ่านค่า 'ค้าง' (ฉันผิด) อะตอมมิกไม่รับประกันความสอดคล้องกันของแคชแบบเต็มเท่านั้น
อัปเดต 2
(5) ลองจินตนาการถึงตัวอย่างนี้ (x = y = 0 และเป็นอะตอม):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
ตามที่เราได้พูดคุยการเห็นข้อความ "msg" ที่ปรากฏบนหน้าจอจะไม่ให้ข้อมูลกับเราเกินกว่า T2 ที่ถูกเรียกใช้หลังจาก T1 ดังนั้นการประหารชีวิตอย่างใดอย่างหนึ่งต่อไปนี้อาจเกิดขึ้น:
- T1 <T3 <T2
- T1 <T2 <T3 (โดยที่ T3 เห็น x = 1 แต่ไม่ใช่ y = 1)
นั่นถูกต้องใช่ไหม?
(6) หากเธรดสามารถอ่านค่า 'เก่า' ได้จะเกิดอะไรขึ้นถ้าเราใช้สถานการณ์ "เผยแพร่" ทั่วไป แต่แทนที่จะส่งสัญญาณว่าข้อมูลบางอย่างพร้อมเราทำตรงกันข้าม (ลบข้อมูล)?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
โดยที่ T2 จะยังคงใช้ ptr ที่ถูกลบจนกว่าจะเห็นว่า is_enabled เป็นเท็จ
(7) นอกจากนี้ความจริงที่ว่าเธรดอาจอ่านค่า 'ค้าง' หมายความว่าmutexไม่สามารถใช้งานได้ด้วยสิทธิ์อะตอมมิกแบบล็อคเดียวใช่หรือไม่ มันจะต้องมีกลไกการซิงค์ระหว่างหัวข้อ มันจะต้องมีอะตอมที่ล็อคได้หรือไม่?