2013-08-06 12 views
31

Pisałem sobie wielowątkowych generator losowyInicjowanie ThreadStatic pole nadal powoduje 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(); 
    } 
} 

jednak rzuca mi NullReferenceException na wypalanie Next(), która nie rozumiem. Czy w jakiś sposób inicjowanie pól ThreadStatic jest zabronione?

Wiem, że mogę sprawdzić, czy pole zostało zainicjowane za każdym razem, ale to nie jest rozwiązanie, którego szukam.

+0

Dlaczego nie używać [ '' Lazy ma wątku bezpieczne opcje] (http://msdn.microsoft.com/en-us/library/ee808725.aspx) – Mgetz

+0

swoje prace kodu bez wyjątku dla mnie. VS2010 \ 4.0 –

+0

Dlaczego nie użyć 'Rngcryptoserviceprovider', który jest ThreadSafe –

Odpowiedz

45

Inicjowanie pól ThreadStatic jest trochę trudne. W szczególności jest to zastrzeżenie:

Nie określić wartości początkowe dla pól oznaczonych ThreadStaticAttribute, ponieważ takie inicjalizacji pojawia się tylko raz, gdy konstruktor klasy wykonuje, a zatem wpływa tylko jeden wątek.

w MSDN Docs. Oznacza to, że wątek uruchomiony podczas inicjowania klasy otrzymuje tę wartość początkową zdefiniowaną w deklaracji pola, ale wszystkie inne wątki mają wartość zerową. Myślę, że właśnie dlatego Twój kod wykazuje niepożądane zachowanie opisane w twoim pytaniu.

Pełniejsze wyjaśnienie znajduje się pod numerem this blog.

(a fragment z bloga)

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

ThreadStatic jest inicjowany w statycznym konstruktora - co wykonuje tylko raz. Tak więc tylko pierwszy wątek jest przypisywany "ciągowi foo ", gdy wykonywany jest konstruktor statyczny. Po przejściu do kolejnych wątków , Foo pozostawia niezainicjowaną wartość pustą.

Najlepszym sposobem obejścia tego problemu jest użycie obiektu w celu uzyskania dostępu do wniosku Foo o numerze: .

[ThreadStatic] 
private static string _foo; 

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

Zauważ, że nie ma potrzeby na blokadę w obiekcie statycznym, ponieważ każdy wątek jest działającą na _foo to tylko do tego wątku. Nie może być rywalizacji z innymi wątkami. Jest to opisane w tym pytaniu: ThreadStatic and Synchronization

+0

Oto odpowiedź, której szukałem. Myślałem, że mogę uniknąć kontroli zerowej. Dziękuję za pomoc. – Tarec

+0

Czy to jest bezpieczne dla gettera? Gdybyś miał dwie metody z powinowactwem do tego samego wątku i obaj nazywali to getter w tym samym czasie, to nie mogłoby to spowodować potencjalnego problemu. Czy nie powinien istnieć zamek? – cost

+2

@cost - Każdy wątek ma własne '_foo', a ten sam wątek nie może uzyskać dostępu do gettera więcej niż jeden raz w tym samym czasie. Zobacz http: // stackoverflow.com/questions/1087599/is-this-a-thread-safe-way-to-initialize-a-threadstatic. Również MSFT mówi: "Wszelkie publiczne statyczne elementy tego typu są bezpieczne dla wątków" http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(v=vs.110).aspx – hatchet

Powiązane problemy