Compartilhar via


volatile (Referência de C#)

A palavra-chave volatile indica que um campo pode ser modificado por vários threads que estão em execução ao mesmo tempo. O compilador, o sistema do runtime e até mesmo o hardware podem reorganizar as leituras e gravações para 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.

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 nenhum 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 palavra-chave volatile pode ser aplicada a campos desses tipos:

  • Tipos de referência.
  • Tipos de ponteiro (em um contexto sem segurança). Observe que embora o ponteiro em si possa ser volátil, o objeto para o 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, float e bool.
  • Um tipo enum com um dos seguintes tipos base: byte, sbyte, short, ushort, int ou uint.
  • Parâmetros de tipo genérico conhecidos por serem tipos de referência.
  • IntPtr e UIntPtr.

Outros tipos, inclusive double e long, não podem ser marcados como volatile, pois as leituras e gravações nos campos desses tipos não podem ser garantidas como 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.

A palavra-chave volatile pode ser aplicada somente aos campos de uma class ou struct. As variáveis locais não podem ser declaradas como volatile.

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 de trabalho ou auxiliar pode ser criado e usado para executar o processamento em paralelo com o do thread primário. Para saber mais sobre multithreading, confira Threading gerenciado.

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 membro _shouldStop, o comportamento é imprevisível. O método DoWork pode otimizar o acesso do 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 da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Confira também