Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
La volatile parola chiave indica che un campo può essere modificato da più thread in esecuzione contemporaneamente. Il compilatore, il sistema di runtime e persino l'hardware possono riorganizzare le letture e le scritture in posizioni di memoria per motivi di prestazioni. I campi dichiarati volatile vengono esclusi da determinati tipi di ottimizzazioni. Non esiste alcuna garanzia di un singolo ordinamento totale di scritture volatili, come visto da tutti i thread di esecuzione. Per altre informazioni, vedere la classe Volatile.
Attenzione
La volatile parola chiave è spesso fraintesa e usata impropriamente nella programmazione multithreaded. Nella maggior parte degli scenari è consigliabile usare alternative più sicure e affidabili anziché volatile. .NET moderno offre strumenti di concorrenza migliori, ad esempio la Interlocked classe, l'istruzione lock o le primitive di sincronizzazione di livello superiore. Queste alternative offrono una semantica più chiara e garanzie più efficaci rispetto a volatile. Prendere in considerazione l'uso volatile solo in scenari rari e avanzati in cui si conoscono appieno le limitazioni e si è verificato che sia la soluzione appropriata.
Annotazioni
In un sistema multiprocessore, un'operazione di lettura volatile non garantisce di ottenere il valore più recente scritto in tale posizione di memoria da qualsiasi processore. Analogamente, un'operazione di scrittura volatile non garantisce che il valore scritto sarebbe immediatamente visibile ad altri processori.
La volatile parola chiave può essere applicata ai campi di questi tipi:
- Tipi di riferimento.
- Tipi di puntatore (in un contesto non sicuro). Si noti che, anche se il puntatore stesso può essere dichiarato volatile, l'oggetto a cui esso punta non può esserlo. In altre parole, non è possibile dichiarare un puntatore a "volatile".
- Tipi semplici, come
sbyte,byte,short,ushort,int,uint,char,floatebool. - Tipo
enumcon uno dei tipi di base seguenti:byte,sbyte,short,ushortint, ouint. - Parametri di tipo generico noti come tipi di riferimento.
- IntPtr e UIntPtr.
Non è possibile contrassegnare double altri tipi, inclusi long e volatile, perché non è possibile garantire che le letture e le scritture in campi di tali tipi siano atomiche. Per proteggere l'accesso multithread a tali tipi di campi, usare i membri della classe Interlocked o proteggere l'accesso usando l'istruzione lock.
Per la maggior parte degli scenari multithreading, anche con tipi supportati, preferire l'uso di Interlocked operazioni, lock istruzioni o altre primitive di volatile sincronizzazione anziché. Queste alternative sono meno soggette a bug di concorrenza sottili.
La volatile parola chiave può essere applicata solo ai campi di un class o struct. Le variabili locali non possono essere dichiarate volatile.
Alternative a volatile
Nella maggior parte dei casi, è consigliabile usare una di queste alternative più sicure anziché volatile:
-
Interlocked operazioni: fornire operazioni atomiche per tipi numerici e assegnazioni di riferimento. Questi sono in genere più veloci e offrono garanzie più forti rispetto a
volatile. -
lockistruzione: Fornisce l'esclusione reciproca e le barriere di memoria. Usare per proteggere sezioni critiche più grandi. -
Volatile classe: fornisce operazioni di lettura e scrittura volatili esplicite con semantica più chiara rispetto alla
volatileparola chiave . - Primitive di sincronizzazione di livello superiore: ad ReaderWriterLockSlimesempio , Semaphoreo raccolte simultanee da System.Collections.Concurrent.
La volatile parola chiave non fornisce atomicità per le operazioni diverse dall'assegnazione, non impedisce le race condition e non fornisce garanzie di ordinamento per altre operazioni di memoria. Queste limitazioni lo rendono non adatto per la maggior parte degli scenari di concorrenza.
Esempio
Nell'esempio seguente viene illustrato come dichiarare una variabile di campo pubblico come volatile.
class VolatileTest
{
public volatile int sharedStorage;
public void Test(int i)
{
sharedStorage = i;
}
}
Nell'esempio seguente viene illustrato come creare e usare un thread ausiliario o di lavoro per eseguire l'elaborazione in parallelo con quella del thread primario. Per ulteriori informazioni sul multithreading, vedere Threading gestito da .NET.
public class Worker
{
// This method is called when the thread is started.
public void DoWork()
{
bool work = false;
while (!_shouldStop)
{
work = !work; // simulate some work
}
Console.WriteLine("Worker thread: terminating gracefully.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Keyword volatile is used as a hint to the compiler that this data
// member is accessed by multiple threads.
private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
public static void Main()
{
// Create the worker thread object. This does not start the thread.
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start();
Console.WriteLine("Main thread: starting worker thread...");
// Loop until the worker thread activates.
while (!workerThread.IsAlive)
;
// Put the main thread to sleep for 500 milliseconds to
// allow the worker thread to do some work.
Thread.Sleep(500);
// Request that the worker thread stop itself.
workerObject.RequestStop();
// Use the Thread.Join method to block the current thread
// until the object's thread terminates.
workerThread.Join();
Console.WriteLine("Main thread: worker thread has terminated.");
}
// Sample output:
// Main thread: starting worker thread...
// Worker thread: terminating gracefully.
// Main thread: worker thread has terminated.
}
Con il volatile modificatore aggiunto alla dichiarazione di _shouldStop sul posto, si otterranno sempre gli stessi risultati (simile all'estratto illustrato nel codice precedente). Tuttavia, senza tale modificatore sul _shouldStop membro, il comportamento è imprevedibile. Il DoWork metodo può ottimizzare l'accesso ai membri, con conseguente lettura di dati non aggiornati. A causa della natura della programmazione multithread, il numero di letture obsolete è imprevedibile. Diverse esecuzioni del programma produrranno risultati leggermente diversi.
Specificazione del linguaggio C#
Per altre informazioni, vedere la specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.