0

シングルトンクラス(ダブルチェックロック)を実装するコードをフォローしました

public sealed class Plugin
{
    #region Private Fields

    private static volatile Plugin _instance;
    private static object syncRoot = new Object();

    private Dictionary<int, string> myMap;

    #endregion

    private Plugin()
    {
        myMap = MapInit(GetMainModuleName());
    }

    static Plugin()
    { }

    public static Plugin Instance
    {
        get 
        {
            if (_instance == null) 
            {
                lock (syncRoot) 
                {
                   if (_instance == null)
                       _instance = new Plugin();
                }
            }

            return _instance;
        }
    }
}

シングルトン インスタンスはデバッグ モードで適切に構築されており、すべて正常に動作しているようです。しかし、リリース モードでは、インスタンスが適切に構築される前に返されます。つまり、myMap は初期化されません。

また、次のコードがデバッグ モードで完全に実行されるまでに約 10 ~ 15 秒かかることに注意してください。

myMap = MapInit(GetMainModuleName());

これはコンパイラの最適化の問題ですか? 助けてください

4

2 に答える 2

1

シングルトンは必要ありません。実際、シングルトンは必要ありません。なぜ人々は最近シングルトンをやっているのですか?

ほら、これは単純に機能します:

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;
        }
    }
}

とはいえ...静的コンストラクターを使用するだけです。

于 2012-10-17T07:04:21.707 に答える
0

わかりました、素朴に聞こえるかもしれませんが、実際の問題は次のとおりです。上記のコードを含む dll が、無効な exe.config を持つメイン アプリケーションにロードされました。また、私のdllには個別のdll.config(有効な)があったため、デバッガーを介して実行するとアプリケーションは正常に動作しましたが、(デバッガーを接続せずに)展開環境で実行すると、無効な構成ファイルの例外が発生しました。

メインの exe.config を有効な構成ファイルとして作成しましたが、現在は機能しています。

したがって、基本的に、解決策は、構築プロセスに例外があるかどうかを確認するのと同じくらい単純です。

于 2012-10-22T11:26:38.260 に答える