2013-08-06 2 views
31

Я написал себе многопоточный генератор случайногоInitializing 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

Почему бы не использовать [ '' Lazy имеет Потокобезопасная опции] (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 .

[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

Безопасна ли эта лента? Если бы у вас было два метода с близостью к одному и тому же потоку, и оба они одновременно называли геттер, это не могло бы вызвать потенциальную проблему. Разве не должно быть замка? – 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

Смежные вопросы