มีวิธีการนำไปใช้โดยไม่ต้องลงvolatile
สนาม ฉันจะอธิบายมัน ...
ฉันคิดว่ามันเป็นการเข้าถึงหน่วยความจำที่เรียงลำดับใหม่ภายในล็อคซึ่งเป็นอันตรายดังนั้นคุณจะได้รับอินสแตนซ์เริ่มต้นที่ไม่สมบูรณ์นอกล็อค เพื่อหลีกเลี่ยงสิ่งนี้ฉันทำสิ่งนี้:
public sealed class Singleton
{
private static Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
var temp = new Singleton();
System.Threading.Thread.MemoryBarrier();
instance = temp;
}
}
}
return instance;
}
}
}
ทำความเข้าใจเกี่ยวกับรหัส
ลองนึกภาพว่ามีรหัสเริ่มต้นบางอย่างอยู่ในตัวสร้างของคลาส Singleton หากคำสั่งเหล่านี้ถูกจัดเรียงใหม่หลังจากที่ตั้งค่าฟิลด์ด้วยที่อยู่ของออบเจ็กต์ใหม่แสดงว่าคุณมีอินสแตนซ์ที่ไม่สมบูรณ์ ... ลองนึกภาพว่าคลาสมีรหัสนี้:
private int _value;
public int Value { get { return this._value; } }
private Singleton()
{
this._value = 1;
}
ลองนึกภาพการโทรไปยังผู้สร้างโดยใช้ตัวดำเนินการใหม่:
instance = new Singleton();
สิ่งนี้สามารถขยายไปสู่การดำเนินการเหล่านี้ได้:
ptr = allocate memory for Singleton;
set ptr._value to 1;
set Singleton.instance to ptr;
จะเกิดอะไรขึ้นถ้าฉันเรียงลำดับคำแนะนำเหล่านี้ใหม่เช่นนี้:
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
set ptr._value to 1;
มันสร้างความแตกต่างหรือไม่? ไม่ถ้าคุณคิดว่ามีเธรดเดียว ใช่ถ้าคุณคิดถึงหลายเธรด ... จะเกิดอะไรขึ้นถ้าเธรดถูกขัดจังหวะหลังจากset instance to ptr
:
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
-- thread interruped here, this can happen inside a lock --
set ptr._value to 1; -- Singleton.instance is not completelly initialized
นั่นคือสิ่งที่อุปสรรคหน่วยความจำหลีกเลี่ยงโดยไม่อนุญาตให้จัดลำดับการเข้าถึงหน่วยความจำใหม่:
ptr = allocate memory for Singleton;
set temp to ptr;
set ptr._value to 1;
-- memory barrier... cannot reorder writes after this point, or reads before it --
-- Singleton.instance is still null --
set Singleton.instance to temp;
ขอให้สนุกกับการเขียนโค้ด!