Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La volatile palabra clave indica que varios subprocesos que se ejecutan al mismo tiempo pueden modificar un campo. El compilador, el sistema en tiempo de ejecución e incluso el hardware pueden reorganizar lecturas y escrituras en ubicaciones de memoria por motivos de rendimiento. Los campos declarados volatile se excluyen de determinados tipos de optimizaciones. No hay ninguna garantía de una única ordenación total de las operaciones de escritura volátiles como se muestra en todos los subprocesos de ejecución. Para obtener más información, vea la clase Volatile.
Precaución
La volatile palabra clave a menudo se malinterpreta y se usa incorrectamente en la programación multiproceso. En la mayoría de los escenarios, debe usar alternativas más seguras y confiables en lugar de volatile. .NET moderno proporciona mejores herramientas de simultaneidad, como la Interlocked clase, la lock instrucción o primitivos de sincronización de nivel superior. Estas alternativas proporcionan una semántica más clara y garantías más sólidas que volatile. Considere la posibilidad de usar volatile solo en escenarios poco frecuentes y avanzados en los que comprende completamente sus limitaciones y ha comprobado que es la solución adecuada.
Nota:
En un sistema multiprocesador, una operación de lectura volátil no garantiza obtener el valor más reciente escrito en esa ubicación de memoria por cualquier procesador. Del mismo modo, una operación de escritura volátil no garantiza que el valor escrito sea visible inmediatamente para otros procesadores.
La volatile palabra clave se puede aplicar a campos de estos tipos:
- Tipos de referencia.
- Tipos de puntero (en un contexto no seguro). Tenga en cuenta que, aunque el propio puntero puede ser volátil, el objeto al que apunta no puede. Es decir, no puede declarar un "puntero a volatile".
- Tipos simples como
sbyte, ,byte,shortushort,intuint, ,charfloaty .bool - Tipo
enumcon uno de los siguientes tipos base:byte,sbyte,short,ushort,intouint. - Parámetros de tipo genérico conocidos como tipos de referencia.
- IntPtr y UIntPtr.
No se pueden marcar volatile otros tipos, incluidos double y long, porque no se puede garantizar que las lecturas y escrituras en campos de esos tipos sean atómicas. Para proteger el acceso multiproceso a esos tipos de campos, use los miembros de clase Interlocked o proteja el acceso mediante la instrucción lock.
Para la mayoría de los escenarios multiproceso, incluso con tipos admitidos, prefiere usar operaciones Interlocked, instrucciones lock u otros primitivos de sincronización en lugar de volatile. Estas alternativas son menos propensas a errores de simultaneidad sutiles.
La palabra clave volatile solo se puede aplicar a los campos de un class o un struct. Las variables locales no se pueden declarar volatile.
Alternativas a la volatilidad
En la mayoría de los casos, debe usar una de estas alternativas más seguras en lugar de volatile:
- Operaciones Interlocked: Proporciona operaciones atómicas para tipos numéricos y asignaciones de referencia. Por lo general, son más rápidas y proporcionan garantías más fuertes que
volatile. - Instrucción
lock: proporciona exclusión mutua y barreras de memoria. Se usa para proteger secciones críticas más grandes. - Volatile clase: proporciona operaciones explícitas de lectura y escritura volátiles con semántica más clara que la
volatilepalabra clave . - Primitivos de sincronización de nivel superior: como ReaderWriterLockSlim, Semaphoreo colecciones simultáneas de System.Collections.Concurrent.
La palabra clave volatile no proporciona atomicidad para las operaciones distintas de la asignación, no impide las condiciones de carrera y no proporciona garantías de ordenación para otras operaciones de memoria. Estas limitaciones hacen que no sea adecuado para la mayoría de los escenarios de simultaneidad.
Ejemplo
En el ejemplo siguiente se muestra cómo declarar una variable de campo pública como volatile.
class VolatileTest
{
public volatile int sharedStorage;
public void Test(int i)
{
sharedStorage = i;
}
}
En el ejemplo siguiente se muestra cómo se puede crear un subproceso auxiliar o de trabajo y usarse para realizar el procesamiento en paralelo con el del subproceso principal. Para obtener más información sobre multithreading, consulte 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.
}
Con el modificador volatile agregado a la declaración de _shouldStop en su lugar, siempre obtendrá los mismos resultados (como en el fragmento que se muestra en el código anterior). Sin embargo, sin ese modificador en el _shouldStop miembro, el comportamiento es impredecible. El DoWork método puede optimizar el acceso de los miembros, lo que da lugar a la lectura de datos obsoletos. Dada la naturaleza de la programación multiproceso, el número de lecturas obsoletas es imprevisible. Diferentes ejecuciones del programa producirán resultados algo diferentes.
Especificación del lenguaje C#
Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es el origen definitivo de la sintaxis y el uso de C#.