2013-08-06 27 views
31

我已经写了自己是一个多线程随机生成初始化ThreadStatic现场还是引起的NullReferenceException

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

但是,我抛出一个NullReferenceException上发射的next(),我不明白。那种初始化ThreadStatic字段是不是被禁止了?

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

+0

为什么不使用['懒惰'它有线程安全的选项(http://msdn.microsoft.com/en-us/library/ee808725.aspx) – Mgetz

+0

您的代码工作对我来说没有例外。 VS2010 \ 4.0 –

+0

为什么不使用'Rngcryptoserviceprovider'这是ThreadSafe –

回答

45

初始化ThreadStatic字段有点棘手。特别是有这样的告诫:

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

MSDN Docs。这意味着当类初始化时运行的线程将获得您在字段声明中定义的初始值,但所有其他线程的值都为null。我认为这就是为什么你的代码展示你的问题中描述的不良行为。

更详细的解释请参见this blog

(从博客摘录)

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

的ThreadStatic在静态构造函数初始化 - 只有 执行一次。所以当静态构造函数执行时,只有第一个线程被分配了“foo ”字符串。在所有后续线程中访问时,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

+0

这就是我一直在寻找的答案。我以为我可以避免空检查。感谢您的帮助。 – Tarec

+0

吸气剂线程安全吗?如果您有两个方法对同一个线程具有亲和性,并且他们都同时调用了getter,则不会导致潜在的问题。不应该有锁吗? – cost

+2

@cost - 每个线程都有自己的'_foo',同一个线程不能同时访问多次。请参阅http:// stackoverflow。com/questions/1087599/is-this-a-thread-safe-way-to-initialize-a-threadstatic。另外MSFT说'这种类型的任何公共静态成员是线程安全的http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(v=vs.110).aspx – hatchet