2015-04-02 17 views
6

Sono molto confuso su questo argomento - se la lettura/commutazione di un valore bool è thread-safe.C# e thread-safety di un bool

// case one, nothing 
    private bool v1; 
    public bool V1 { get { return v1; } set { v1 = value; } } 

    // case two, with Interlocked on set 
    private int v2; 
    public int V2 { get { return v2; } set { Interlocked.Exchange(ref v2, value); } } 

    // case three, with lock on set 
    private object fieldLock = new object(); 
    private bool v3; 
    public bool V3 { get { return v3; } set { lock (fieldLock) v3 = value; } } 

Sono tutti thread-safe?

EDIT

Da quello che ho letto (click) atomicità di bool non garantisce che sarà thread-safe. Sarà allora l'aiuto del tipo volatile?

+1

Nessuno di questi è thread-safe. Il thread che chiama il getter legge sempre un valore scaduto. Quanto stantio dipende dal processore e dall'ottimizzatore. Varia da una manciata di nanosecondi all'infinito. Anche il getter deve essere sincronizzato. Oppure useresti ManualResetEvent/Slim. –

+0

@ Ksv3n * L'assegnazione è sempre un'operazione atomica * False, l'assegnazione 'long' (64 bit) da parte di un programma a 32 bit non è atomica. – xanatos

+0

@HansPassant Un getter può leggere un vecchio valore, questo è comprensibile. Tuttavia, come poteva durare all'infinito? L'uso di un lucchetto farà leggere il valore * corretto * - rileggerlo come se fosse volatile? Non penso che funzioni in questo modo, sono confuso su quello che stai dicendo. Ho pensato che una volta che nessuno sta scrivendo più, il getter funzionerà correttamente. –

risposta

12

No, non tutti sono thread-safe.

Il caso uno non è in realtà completamente thread-safe, o meglio dire - non è thread-safe affatto. Anche se le operazioni con booleano sono atomiche, il valore variabile può essere memorizzato in una cache, e quindi, come nella CPU multicore ogni core ha la propria cache, il valore può essere potenzialmente danneggiato.

Andando ancora oltre, il compilatore e la CPU possono eseguire alcune ottimizzazioni interne, incluso il riordino delle istruzioni, che può influire negativamente sulla logica del programma.

È possibile aggiungere la parola chiave volatile per notificare al compilatore che questo campo è utilizzato in un contesto a più thread. Risolverà i problemi con la cache e il riordino delle istruzioni, ma non fornisce un codice veramente "thread-safe" (poiché le operazioni di scrittura non saranno ancora sincronizzate). Anche volatile non può essere applicato alla variabile locale.

Quindi, quando si ha a che fare con il multi-threading, bisogna sempre usare una tecnica di sincronizzazione dei thread su risorse preziose.

Per ulteriori informazioni, consultare la risposta this, che offre una spiegazione più approfondita delle diverse tecniche. (esempio c'è circa int, ma non importa, descrive l'approccio generale.)

+3

* il valore può essere potenzialmente danneggiato * No, il valore non può essere danneggiato (come in "trasformazione in valore non valido/non definito"). Potresti ottenere valori vecchi ("vecchi"). – xanatos

+0

Intendo "corrotto" in un modo "non uno, che dovrebbe essere o ci si aspetta che sia". Sicuramente non è danneggiato in un senso di corruzione della memoria :) – Yura