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;
}
ここで、new演算子を使用したコンストラクターの呼び出しを想像してください。
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;
それは違いを生みますか?あなたが単一のスレッドについて考えるならば、いいえ。複数のスレッドについて考える場合はYES ...スレッドが直後に中断された場合はどうなりますか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;
ハッピーコーディング!