แย่ที่สุด (ใช้งานไม่ได้จริง)
เปลี่ยน accessifier ของcounter
เป็นpublic volatile
อย่างที่คนอื่น ๆ พูดถึงสิ่งนี้ด้วยตัวของมันเองไม่ได้ปลอดภัยอะไรเลย ประเด็นvolatile
ก็คือว่ามีหลายเธรดที่ทำงานบน CPU หลายตัวสามารถและจะแคชข้อมูลและคำสั่งซื้อซ้ำ
หากไม่ใช่ volatile
และ CPU A เพิ่มค่าหน่วยความจำ CPU B อาจไม่เห็นค่าที่เพิ่มขึ้นจริงจนกระทั่งบางครั้งในภายหลังซึ่งอาจทำให้เกิดปัญหา
หากเป็นvolatile
เช่นนี้จะช่วยให้มั่นใจว่าทั้งสองซีพียูสามารถดูข้อมูลเดียวกันได้ในเวลาเดียวกัน มันไม่ได้หยุดพวกเขาจากการสอดแทรกการอ่านและการเขียนซึ่งเป็นปัญหาที่คุณพยายามหลีกเลี่ยง
อันดับสอง:
lock(this.locker) this.counter++
;
วิธีนี้ปลอดภัยที่จะทำ (หากคุณจำได้ในlock
ทุกที่ที่คุณเข้าถึงthis.counter
) จะป้องกันไม่ให้กระทู้อื่น ๆ locker
จากการดำเนินการรหัสอื่นใดที่จะรักษาโดย การใช้ตัวล็อคยังช่วยป้องกันปัญหาการเรียงลำดับใหม่ของ CPU แบบหลายจุดซึ่งดีมาก
ปัญหาคือล็อคช้าและถ้าคุณใช้locker
ในที่อื่นที่ไม่เกี่ยวข้องจริงๆคุณสามารถปิดกั้นเธรดอื่น ๆ ได้โดยไม่มีเหตุผล
ดีที่สุด
Interlocked.Increment(ref this.counter);
วิธีนี้ปลอดภัยเนื่องจากการอ่านเพิ่มและเขียนใน 'หนึ่งครั้ง' อย่างมีประสิทธิภาพซึ่งไม่สามารถถูกขัดจังหวะได้ ด้วยเหตุนี้จึงไม่ส่งผลกระทบต่อรหัสอื่น ๆ และคุณไม่จำเป็นต้องจำที่จะล็อคที่อื่นเช่นกัน นอกจากนี้ยังเร็วมาก (อย่างที่ MSDN บอกว่าสำหรับซีพียูสมัยใหม่นี่มักจะเป็นคำสั่ง CPU เพียงตัวเดียว)
ฉันไม่แน่ใจทั้งหมด แต่ถ้ามันได้รับรอบ CPU อื่น ๆ เรียงลำดับสิ่งต่าง ๆ หรือถ้าคุณยังต้องรวมความผันผวนกับการเพิ่มขึ้น
InterlockedNotes:
- วิธีที่ถูกล็อคจะปลอดภัยอย่างต่อเนื่องในจำนวนแกนหรือซีพียูใด ๆ
- วิธีการเชื่อมต่อใช้รั้วเต็มรอบคำแนะนำพวกเขาดำเนินการดังนั้นการสั่งซื้อใหม่จะไม่เกิดขึ้น
- ไม่จำเป็นต้องใช้วิธีการที่เชื่อมต่อกันหรือแม้กระทั่งไม่สนับสนุนการเข้าถึงเขตข้อมูลที่มีความผันผวนเนื่องจากความผันผวนจะถูกวางครึ่งรั้วรอบการดำเนินการในสนามที่กำหนด
เชิงอรรถ: ความผันผวนที่ดีจริงๆ
เช่นเดียวกับที่volatile
ไม่ได้ป้องกันปัญหาการมัลติเธรดเหล่านี้มันมีไว้เพื่ออะไร? ตัวอย่างที่ดีคือการพูดว่าคุณมีสองกระทู้หนึ่งซึ่งมักจะเขียนถึงตัวแปร (พูดqueueLength
) และหนึ่งที่มักจะอ่านจากตัวแปรเดียวกันนั้น
หากqueueLength
ไม่ระเหยเธรด A อาจเขียนห้าครั้ง แต่เธรด B อาจเห็นการเขียนเหล่านั้นว่าล่าช้า (หรืออาจเกิดขึ้นในลำดับที่ไม่ถูกต้อง)
วิธีแก้ปัญหาคือล็อค แต่คุณสามารถใช้ความผันผวนในสถานการณ์นี้ได้ สิ่งนี้จะช่วยให้มั่นใจได้ว่าเธรด B จะเห็นสิ่งที่ทันสมัยที่สุดเสมอที่เธรด A เขียนไว้ โปรดทราบว่าตรรกะนี้ใช้ได้เฉพาะในกรณีที่คุณมีนักเขียนที่ไม่เคยอ่านและผู้อ่านที่ไม่เคยเขียนและหากสิ่งที่คุณเขียนเป็นค่าอะตอมมิก ทันทีที่คุณอ่าน - แก้ไข - เขียนเดียวคุณจะต้องไปที่การดำเนินการ Interlocked หรือใช้ล็อค