Partilhar via


volátil (Referência C#)

Use a volatile palavra-chave para indicar que um campo pode ser modificado por múltiplas threads que estão a executar ao mesmo tempo. Por razões de desempenho, o compilador, o sistema de execução e até o hardware podem reorganizar leituras e escritas para locais de memória. Declarar um campo como volatile exclui-lo de certos tipos de otimizações. Não há garantia de uma única ordem total de escritas voláteis, como se vê em todos os threads de execução. Para obter mais informações, consulte a classe Volatile.

Atenção

A volatile palavra-chave é muitas vezes mal compreendida e mal utilizada na programação multithreaded. Na maioria dos cenários, use alternativas mais seguras e fiáveis em vez de volatile. O .NET moderno fornece melhores ferramentas de simultaneidade, como a Interlocked classe, a lock instrução ou primitivas de sincronização de nível superior. Estas alternativas fornecem semânticas mais claras e garantias mais fortes do que volatile. Considere usar volatile apenas em cenários raros e avançados, onde você entende totalmente suas limitações e verificou que é a solução apropriada.

Observação

Num sistema multiprocessador, uma operação de leitura volátil não garante obter o valor mais recente escrito nessa localização de memória por qualquer processador. De forma semelhante, uma operação de escrita volátil não garante que o valor escrito seja imediatamente visível para outros processadores.

A referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em versões preliminares públicas para a próxima versão da linguagem.

A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.

Sugestão

Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.

Aplique a volatile palavra-chave a campos destes tipos:

  • Tipos de referência.
  • Tipos de ponteiro (num contexto inseguro). Embora o próprio ponteiro possa ser volátil, o objeto para o qual aponta não pode ser. Ou seja, não se pode declarar um "indicador de volátil".
  • Tipos simples como sbyte, byte, short, ushort, int, uint, char, float e bool.
  • Um enum tipo com um dos seguintes tipos básicos: byte, sbyte, short, ushort, int, ou uint.
  • Parâmetros de tipo genéricos conhecidos por serem tipos de referência.
  • IntPtr e UIntPtr.

Não podes marcar outros tipos, incluindo double e long, porque volatile as leituras e escritas em campos desses tipos não podem ser garantidas como atómicas. Para proteger o acesso multithread a esses tipos de campos, use os Interlocked membros da classe ou proteja o acesso usando a lock instrução.

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

Só pode aplicar a volatile palavra-chave aos campos de a class ou struct. Não podes declarar variáveis locais como volatile.

Alternativas à volatilidade

Na maioria dos casos, utilize uma destas alternativas mais seguras em vez de volatile:

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

A volatile palavra-chave não fornece atomicidade para operações para além da atribuição. Não previne condições de corrida, nem garante ordenação para outras operações de memória. Essas limitações o tornam inadequado para a maioria dos cenários de simultaneidade.

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 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.
}

Quando adiciona o volatile modificador à declaração de _shouldStop, obtém sempre os mesmos resultados (semelhantes ao excerto 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 dos membros, resultando na leitura de dados obsoletos. Devido à natureza da programação multithread, o número de leituras obsoletas é imprevisível. Diferentes sequências do programa produzem resultados algo distintos.

Especificação da linguagem C#

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

Ver também