Compartilhar via


volatile (Documentação de C#)

A volatile palavra-chave indica que um campo pode ser modificado por vários threads que estão sendo executados ao mesmo tempo. O compilador, o sistema de runtime e até mesmo o hardware podem reorganizar leituras e gravações em locais de memória por motivos de desempenho. Os campos declarados volatile são excluídos de determinados tipos de otimizações. Não há nenhuma garantia de uma única ordenação total de gravações voláteis como visto em todos os threads de execução. Para obter mais informações, consulte a classe Volatile.

Cuidado

A volatile palavra-chave geralmente é mal compreendida e mal utilizada na programação multithreaded. Na maioria dos cenários, você deve usar alternativas mais seguras e confiáveis em vez de volatile. O .NET moderno fornece melhores ferramentas de simultaneidade, como a Interlocked classe, a lock instrução ou os primitivos de sincronização de nível superior. Essas alternativas fornecem semântica mais clara e garantias mais fortes do que volatile. Considere usar volatile apenas em cenários avançados raros em que você entenda completamente suas limitações e tenha verificado que é a solução apropriada.

Observação

Em um sistema multiprocessador, uma operação de leitura volátil não garante obter o valor mais recente gravado nesse local de memória por qualquer processador. Da mesma forma, uma operação de gravação volátil não garante que o valor gravado seja imediatamente visível para outros processadores.

A volatile palavra-chave pode ser aplicada a campos destes tipos:

  • Tipos de referência.
  • Tipos de ponteiro (em um contexto não seguro). Observe que, embora o ponteiro em si possa ser volátil, o objeto ao qual ele aponta não pode. Em outras palavras, você não pode declarar um "ponteiro como volátil".
  • Tipos simples, como sbyte, byte, short, ushort, int, , uint, char, , floate bool.
  • Um enum tipo com um dos seguintes tipos base: byte, , sbyte, short, ushort, , intou uint.
  • Parâmetros de tipo genérico conhecidos como tipos de referência.
  • IntPtr e UIntPtr.

Outros tipos, incluindo double e long, não podem ser marcados volatile porque leituras e gravações em campos desses tipos não podem ter a garantia de serem atômicas. Para proteger o acesso multithreaded a esses tipos de campo, use os membros da classe Interlocked ou proteja o acesso usando a instrução lock.

Para a maioria dos cenários multithread, mesmo com tipos suportados, prefira usar operações Interlocked, instruções lock ou outros primitivos de sincronização em vez de volatile. Essas alternativas são menos propensas a bugs sutis de concorrência.

A volatile palavra-chave só pode ser aplicada a campos de um class ou struct. Variáveis locais não podem ser declaradas volatile.

Alternativas a voláteis

Na maioria dos casos, você deve usar uma destas alternativas mais seguras em vez de volatile:

  • Interlocked operações: forneça operações atômicas para tipos numéricos e atribuições de referência. Geralmente, elas são mais rápidas e fornecem garantias mais fortes do que volatile.
  • lock instrução: Fornece exclusão mútua e barreiras de memória. Use para proteger seções críticas maiores.
  • Volatile classe: fornece operações de leitura e gravação voláteis explícitas com semântica mais clara do que a volatile palavra-chave.
  • Primitivos de sincronização de nível superior: como ReaderWriterLockSlim, Semaphoreou coleções simultâneas de System.Collections.Concurrent.

A palavra-chave volatile não fornece atomicidade para operações que não sejam atribuição, não impede condições de corrida e não fornece garantias de ordenação para outras operações de memória. Essas limitações o tornam inadequado para a maioria dos cenários de simultaneidade.

Exemplo

O exemplo a seguir mostra como declarar uma variável de campo público como volatile.

class VolatileTest
{
    public volatile int sharedStorage;

    public void Test(int i)
    {
        sharedStorage = i;
    }
}

O exemplo a seguir demonstra como um thread auxiliar ou de trabalho pode ser criado e usado para executar o processamento em paralelo com o do thread primário. Para obter mais informações 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.
}

Com o modificador volatile adicionado à declaração de _shouldStop definida, você sempre obterá os mesmos resultados (semelhante ao trecho mostrado no código anterior). No entanto, sem esse modificador no _shouldStop membro, o comportamento é imprevisível. O DoWork método pode otimizar o acesso ao membro, resultando na leitura de dados obsoletos. Devido à natureza da programação multithreaded, o número de leituras obsoletas é imprevisível. Diferentes execuções do programa produzirão resultados um pouco diferentes.

Especificação da linguagem C#

Para obter mais informações, consulte a Especificação da Linguagem C# . A especificação de idioma é a fonte definitiva para a sintaxe e o uso de C#.

Consulte também