シングルトンは必要ありません。実際、シングルトンは必要ありません。なぜ人々は最近シングルトンをやっているのですか?
ほら、これは単純に機能します:
public sealed class Plugin
{
private static readonly Plugin _instance;
private /*readonly?*/ Dictionary<int, string> myMap;
private Plugin()
{
myMap = MapInit(GetMainModuleName());
}
static Plugin()
{
_instance = new Plugin();
}
public static Plugin Instance
{
get
{
return _instance;
}
}
}
静的コンストラクターは、アプリケーション ドメインごとに 1 回だけ実行されることが保証されています。これは、C# 言語仕様の一部です。
あなたの質問に対処するために、マシンがハードウェアに複数のスレッドを持っている場合、コンパイラの最適化では機能しないことを示したように、ダブル チェック パターンに問題があります。その理由は・・・
[ http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspxから]
メモリ モデルでは、シングル スレッドの観点から変更が認識されない限り、不揮発性の読み取り/書き込みを並べ替えることができます。
でもvolatile。キーワードは、フィールドの読み取り後にフィールドへの書き込みを行う必要があることvolatileをコンパイラーに伝えています。それでも、最初に の値を読み取る前に、新しいオブジェクトを初期化することを妨げるものは何もありません。_instance_instancePlugin_instance
それとは別に、あなたは別の問題に直面していると言いました:
インスタンスが適切に構築される前に返される
次に、初期化が開始されたかどうかを確認するだけでなく、初期化が完了するまで待つ必要があります。明らかに、クラス Plugin のコンストラクターが終了する前にフィールド_instanceが設定されています。その場合は、完了するまで待つ必要があることを意味します。また、非同期呼び出しがいくつかある場合は、「準備完了」プロパティまたは待機する他の方法を追加する必要がある場合があります[オブジェクトが無効な状態になることを許可するのはあなたの責任です]。
*: これは多くの場合、時間変数を導入することで解決されます。この変数に新しいインスタンスを設定し、その変数をフィールドに割り当てます。この手法では、メモリ バリアを追加することでフィールドを非揮発性にすることもできますが、コンストラクタを複数回実行するリスクが高くなります。だから、私はそれをすべてスキップしました。
両方の問題に対処するには、Interlocked と ManualResetEvent のこの組み合わせを使用できます [コンストラクターの内部構造を知らなければ、もっとできるとは思えません]:
public sealed class Plugin
{
private static readonly Plugin _instance;
private static int _initializing;
private static ManualReserEvent _done;
private Dictionary<int, string> myMap;
private Plugin()
{
myMap = MapInit(GetMainModuleName());
}
static Plugin()
{
_done = new ManualResetEvent(false);
}
public static Plugin Instance
{
get
{
if (Interlocked.CompareExchance(ref _initializing, 1, 0) == 0)
{
_instance = new Plugin();
_done.Set();
}
else
{
_done.WaitOne();
}
return _instance;
}
}
}
とはいえ...静的コンストラクターを使用するだけです。