Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
Le volatile mot clé indique qu’un champ peut être modifié par plusieurs threads qui s’exécutent en même temps. Le compilateur, le système d’exécution et même le matériel peuvent réorganiser les lectures et les écritures dans des emplacements de mémoire pour des raisons de performances. Les champs déclarés volatile sont exclus de certains types d’optimisations. Rien ne garantit que les écritures volatiles présentent un ordre total unique, tel que le voient tous les threads d’exécution. Pour plus d'informations, consultez la classe Volatile.
Avertissement
Le mot clé volatile est souvent incompris et mal utilisé dans la programmation multithread. Dans la plupart des scénarios, vous devez utiliser des alternatives plus sûres et plus fiables au lieu de volatile. Le .NET moderne fournit de meilleurs outils d’accès concurrentiel comme la Interlocked classe, l’instruction lock ou les primitives de synchronisation de niveau supérieur. Ces alternatives offrent une sémantique plus claire et des garanties plus fortes que volatile. Envisagez d’utiliser volatile uniquement dans des scénarios rares et avancés où vous comprenez pleinement ses limitations et que vous avez vérifié qu’il s’agit de la solution appropriée.
Remarque
Sur un système multiprocesseur, une opération de lecture volatile ne garantit pas d’obtenir la dernière valeur écrite à cet emplacement de mémoire par n’importe quel processeur. De même, une opération d’écriture volatile ne garantit pas que la valeur écrite serait immédiatement visible par d’autres processeurs.
Le volatile mot clé peut être appliqué aux champs de ces types :
- Types de référence.
- Types de pointeurs (dans un contexte non sécurisé). Notez que même si le pointeur lui-même peut être volatile, l’objet auquel il pointe ne peut pas. En d’autres termes, vous ne pouvez pas déclarer un « pointeur vers une donnée volatile ».
- Types simples tels que
sbyte,byteshortushort,intuintcharfloatet .bool - Type
enumavec l’un des types de base suivants :byte, ,sbyteshort,ushort,int, ouuint. - Paramètres de type générique connus pour être des types de référence.
- IntPtr et UIntPtr.
D’autres types, y compris double et long, ne peuvent pas être marqués volatile , car les lectures et les écritures dans les champs de ces types ne peuvent pas être garantis comme atomiques. Pour protéger l’accès multithread à ces types de champs, utilisez les membres de classe Interlocked ou l’instruction lock.
Pour la plupart des scénarios multithreads, même avec des types pris en charge, préférez utiliser les opérations Interlocked, les instructions lock ou d’autres primitives de synchronisation au lieu de volatile. Ces alternatives sont moins sujettes à des bogues de concurrence subtils.
Le volatile mot clé ne peut être appliqué qu’aux champs d’un class ou struct. Les variables locales ne peuvent pas être déclarées volatile.
Alternatives à volatile
Dans la plupart des cas, vous devez utiliser l’une de ces alternatives plus sûres au lieu de volatile:
-
Interlocked opérations : fournissez des opérations atomiques pour les types numériques et les affectations de référence. Ils sont généralement plus rapides et fournissent des garanties plus fortes que
volatile. -
lockstatement : fournit des barrières mutuelles d’exclusion et de mémoire. Permet de protéger des sections critiques plus grandes. -
Volatile classe : fournit des opérations de lecture et d’écriture volatiles explicites avec une sémantique plus claire que le
volatilemot clé. - Primitives de synchronisation de niveau supérieur : telles que ReaderWriterLockSlim, Semaphoreou collections simultanées à partir de System.Collections.Concurrent.
Le volatile mot clé ne fournit pas d’atomicité pour les opérations autres que l’affectation, n’empêche pas les conditions de concurrence et ne garantit pas l'ordonnancement des autres opérations de mémoire. Ces limitations ne conviennent pas à la plupart des scénarios d’accès concurrentiel.
Exemple :
L’exemple suivant montre comment déclarer une variable de champ public en tant que volatile.
class VolatileTest
{
public volatile int sharedStorage;
public void Test(int i)
{
sharedStorage = i;
}
}
L’exemple suivant montre comment un thread auxiliaire ou worker peut être créé et utilisé pour effectuer le traitement en parallèle avec celui du thread principal. Pour plus d’informations sur la multithreading, consultez Managed Threading.
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.
}
Avec le volatile modificateur ajouté à la déclaration de _shouldStop en vigueur, vous obtiendrez toujours les mêmes résultats (similaire à l’extrait montré dans le code précédent). Toutefois, sans ce modificateur sur le _shouldStop membre, le comportement est imprévisible. La DoWork méthode peut optimiser l’accès aux membres, ce qui entraîne la lecture des données obsolètes. En raison de la nature de la programmation multithread, le nombre de lectures obsolètes est imprévisible. Différentes exécutions du programme produisent des résultats légèrement différents.
Spécification du langage C#
Pour plus d'informations, voir la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.