初始化ThreadStatic字段仍然会导致NullReferenceException

本文关键字:NullReferenceException ThreadStatic 字段 初始化 | 更新日期: 2023-09-27 18:26:35

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

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

我知道我每次都可以检查字段是否初始化,但这不是我想要的解决方案。

初始化ThreadStatic字段仍然会导致NullReferenceException

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

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

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。不可能与其他线程发生争用。这包含在这个问题中:线程静态和同步

上一个答案对于问题的原因是正确的。

如果可以使用.NET 4或更高版本,请使用ThreadLocal,因为它是用初始化器构建的。

请参阅ThreadStatic v.s.ThreadLocal<T>:泛型比属性好吗?

这样就不需要每次读取都使用访问器重载或null检查。