volatile (Справочник по C#)

Ключевое слово volatile означает, что поле может изменить несколько потоков, выполняемых одновременно. Компилятор, среда выполнения или даже аппаратное обеспечение могут изменять порядок операций чтения и записи в расположения в памяти для повышения производительности. Поля, которые объявлены volatile, исключаются из некоторых типов оптимизации. Нет никакой гарантии единого общего прядка временных записей во всех потоках выполнения. Дополнительные сведения см. в описании класса Volatile.

Примечание

В многопроцессорной системе переменные операции чтения не гарантируют получения последнего значения, записанного в эту область памяти любым из процессоров. Аналогичным образом, операция записи не гарантирует, что записываемое значение будет сразу отображаться для других процессоров.

Ключевое слово volatile может применяться к полям следующих типов:

  • Ссылочные типы.
  • Типы указателей (в небезопасном контексте). Несмотря на то, что сам указатель может быть изменяемым, объект, на который он указывает, должен быть постоянным. Другими словами, объявить указатель на изменяемый объект невозможно.
  • Простые типы, например sbyte, byte, short, ushort, int, uint, char, float и bool.
  • Тип enum с одним из следующих базовых типов: byte, sbyte, short, ushort, int или uint.
  • Параметры универсального типа называются ссылочными типами.
  • IntPtr и UIntPtr.

Другие типы, включая double и long, нельзя снабдить модификатором volatile, потому что для них не гарантируется атомарность операций чтения и записи. Чтобы защитить многопотоковый доступ к полям таких типов, используйте члены класса Interlocked или защиту доступа с помощью инструкции lock.

Ключевое слово volatile можно применять только к полям class или struct. Локальные переменные не могут объявляться как volatile.

Пример

В следующем примере показано, как объявить переменную поля открытого типа volatile.

class VolatileTest
{
    public volatile int sharedStorage;

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

Следующий пример демонстрирует создание вспомогательного или рабочего потока и его применение для выполнения обработки параллельно с обработкой основного потока. Дополнительные сведения о многопоточности см. в разделе Управляемая поточность.

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

Добавив модификатор volatile к объявлению _shouldStop, вы всегда получите одинаковые результаты (как показано в приведенном выше фрагменте кода). Но если член _shouldStop не имеет этого модификатора, поведение будет непредсказуемым. Метод DoWork может оптимизировать доступ к членам, что приведет к чтению устаревших данных. В условиях многопоточного программирования невозможно прогнозировать число операций чтения устаревших данных. При каждом запуске программы результаты могут отличаться.

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также