Qui potrei saltare la pistola ma mi sembra che tu stia confondendo due problemi qui.
Uno è l'atomicità, che nella mia mente significa che una singola operazione (che potrebbe richiedere più passaggi) non dovrebbe entrare in conflitto con un'altra operazione di questo tipo.
L'altro è la volatilità, quando è previsto che questo valore cambi e perché.
Prendere il primo. Se la tua operazione in due passaggi richiede di leggere il valore corrente, modificarlo e scriverlo di nuovo, sicuramente vorrai un blocco, a meno che l'intera operazione non possa essere tradotta in una singola istruzione della CPU che possa funzionare su un singola riga di dati della cache.
Tuttavia, il secondo problema è che, anche quando si sta eseguendo il blocco, cosa vedranno gli altri thread.
Un campo volatile
in .NET è un campo che il compilatore sa può cambiare in momenti arbitrari. In un mondo a thread singolo, il cambiamento di una variabile è qualcosa che accade ad un certo punto in un flusso sequenziale di istruzioni in modo che il compilatore sappia quando ha aggiunto il codice che lo modifica, o almeno quando ha chiamato al mondo esterno che può o non può averlo cambiato in modo tale che una volta che il codice ritorna, potrebbe non essere lo stesso valore che era prima della chiamata.
Questa conoscenza consente al compilatore di sollevare il valore dal campo in un registro una volta, prima di un ciclo o un simile blocco di codice e di non rileggere mai il valore dal campo per quel particolare codice.
Con multi-threading, tuttavia, ciò potrebbe causare alcuni problemi. Un thread potrebbe aver regolato il valore e un altro thread, a causa dell'ottimizzazione, non leggerà questo valore per un po 'di tempo, perché lo conosce non è stato modificato.
Quindi quando contrassegni un campo come volatile
stai dicendo al compilatore che non dovrebbe presumere che abbia il valore corrente di questo in qualsiasi momento, eccetto per l'acquisizione di istantanee ogni volta che ha bisogno del valore.
I blocchi risolvono operazioni a più fasi, la volatilità gestisce il modo in cui il compilatore memorizza il valore del campo in un registro e insieme risolveranno più problemi.
Si noti inoltre che se un campo contiene qualcosa che non può essere letto in una singola istruzione cpu, molto probabilmente si vorrà bloccare anche l'accesso in lettura ad esso.
Ad esempio, se si utilizza una CPU a 32 bit e si scrive un valore a 64 bit, l'operazione di scrittura richiederà due passaggi e se un altro thread su un'altra CPU riesce a leggere il 64-bit il valore prima che il passaggio 2 sia completato, otterrà metà del valore precedente e metà del nuovo, ben mescolato insieme, il che può essere anche peggiore rispetto a ottenere uno obsoleto.
Modifica: Per rispondere il commento, che volatile
garantisce l'atomicità delle operazioni di lettura/scrittura, che è bene, vero, in un certo senso, perché la parola chiave volatile
non può essere applicato ai campi di dimensioni superiori 32-bit, in effetti rendendo il campo single-cpu-instruction leggibile/scrivibile su cpu sia a 32 che a 64 bit.E sì, impedirà che il valore venga tenuto in un registro il più possibile.
Quindi parte del commento è errata, volatile
non può essere applicato a valori a 64 bit.
Si noti inoltre che volatile
ha alcune semantiche relative al riordino delle letture/scritture.
Per informazioni importanti, vedere MSDN documentation o C# specification, trovato here, sezione 10.5.3.
È * una lettura abbastanza buona (e sicuramente merita un +1), ma di solito è preferibile che le risposte SO siano alquanto autonome. Sarebbe bello se la tua risposta contenesse un riassunto delle parti importanti di quell'articolo, solo così (1) le persone che sfiorano la pagina lo vedranno e (2) in modo che la risposta rimanga pertinente se l'articolo viene spostato o rimosso. – jalf