初始化ThreadStatic字段仍会导致NullReferenceException

时间:2013-08-06 17:03:27

标签: c# random thread-safety threadstatic

我自己编写了一个多线程随机生成器

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字段是否被禁止?

我知道我可以检查该字段是否每次都被初始化,但这不是我正在寻找的解决方案。

2 个答案:

答案 0 :(得分:58)

初始化ThreadStatic字段有点棘手。特别值得注意的是:

  

不要为标有的字段指定初始值   ThreadStaticAttribute,因为这样的初始化只发生一次,   当类构造函数执行时,因此只影响一个   线程。

MSDN Docs中的

。这意味着初始化类时运行的线程获取您在字段声明中定义的初始值,但所有其他线程的值都为null。我认为这就是为什么你的代码表现出你问题中描述的不良行为的原因。

更全面的解释是this blog

(博客摘录)

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

ThreadStatic在静态构造函数中初始化 - 仅限于此   执行一次。所以只有第一个线程被赋予“foo”   string“当静态构造函数执行时。全部访问时   后续线程,Foo保留在未初始化的空值。

     

解决此问题的最佳方法是使用属性来访问   Foo prop。

[ThreadStatic]
private static string _foo;

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

请注意,静态属性中不需要锁定,因为每个线程都作用于仅适用于该线程的_foo。其他线程无法争用。这个问题包含在这个问题中:ThreadStatic and Synchronization

答案 1 :(得分:1)

先前的答案对于问题的原因是正确的。

如果您可以使用.NET 4或更高版本,请使用ThreadLocal代替初始化程序构建的版本。

请参见ThreadStatic v.s. ThreadLocal<T>: is generic better than attribute?

然后,您不需要每次读取都进行访问器重载或null检查。