51

マルチスレッドの乱数発生器を自分で作成しました

public static class MyRandGen
{
    private static Random GlobalRandom = new Random();
    [ThreadStatic]
    private static Random ThreadRandom = new Random(SeedInitializer());
    private static int SeedInitializer()
    {
        lock (GlobalRandom) return GlobalRandom.Next();
    }

    public static int Next()
    {
        return ThreadRandom.Next();
    }
}

ただし、Next() を起動すると NullReferenceException がスローされますが、これは理解できません。そのような ThreadStatic フィールドの初期化はどういうわけか禁止されていますか?

フィールドが毎回初期化されているかどうかを確認できることはわかっていますが、それは私が探している解決策ではありません。

4

2 に答える 2

79

ThreadStatic フィールドの初期化は少しトリッキーです。特に、次の注意事項があります。

ThreadStaticAttribute でマークされたフィールドには初期値を指定しないでください。このような初期化は、クラス コンストラクターの実行時に 1 回だけ行われ、1 つのスレッドにしか影響しないためです。

MSDN ドキュメントで。つまり、クラスの初期化時に実行されているスレッドは、フィールド宣言で定義した初期値を取得しますが、他のすべてのスレッドの値は null になります。これが、質問で説明されている望ましくない動作をコードが示している理由だと思います。

詳細な説明は、このブログにあります。

(ブログからの抜粋)

[ThreadStatic]
private static string Foo = "the foo string";

ThreadStatic は、一度だけ実行される静的コンストラクターで初期化されます。そのため、静的コンストラクターの実行時に、最初のスレッドのみに「foo 文字列」が割り当てられます。後続のすべてのスレッドでアクセスされると、Foo は初期化されていない null 値のままになります。

これを回避する最善の方法は、プロパティを使用して Foo プロパティにアクセスすることです。

[ThreadStatic]
private static string _foo;

public static string Foo {
   get {
     if (_foo == null) {
         _foo = "the foo string";
     }
     return _foo;
   }
}

各スレッドはそのスレッド専用の に基づいて動作するため、静的プロパティでロックする必要がないことに注意してください_foo。他のスレッドと競合することはありません。これについては、次の質問で説明します: ThreadStatic と同期

于 2013-08-06T17:18:34.487 に答える