volatile (C# リファレンス)

volatile キーワードは、同時に実行されている複数のスレッドによって、フィールドが変更される可能性があることを示します。 コンパイラ、ランタイム システム、さらにはハードウェアで、パフォーマンスを上げる目的でメモリの場所との読み書きを再整理できます。 volatile と宣言されているフィールドは、特定の種類の最適化から除外されています。 volatile 書き込みの単一の全体的順序がすべての実行スレッドから認識される保証はありません。 詳細については、Volatile クラスを参照してください。

注意

マルチプロセッサ システムでの揮発性読み取り操作では、任意のプロセッサがそのメモリ位置に書き込んだ最新の値を取得することが保証されません。 同様に、揮発性書き込み操作では、書き込まれた値が他のプロセッサにすぐに表示されることが保証されません。

volatile キーワードは次の型のフィールドに使用できます。

  • 参照型。
  • ポインター型 (unsafe コンテキスト内)。 ポインター自体は volatile にできますが、ポインターが指しているオブジェクトは volatile にできません。 つまり、"volatile を指すポインター" は宣言できません。
  • sbytebyteshortushortintuintcharfloatbool など、単純型。
  • 基本型が bytesbyteshortushortintuint のいずれかの enum 型。
  • 参照型であることが判明しているジェネリック型パラメーター。
  • IntPtr および UIntPtr

doublelong など、その他の型には volatile を指定できません。そのような型のフィールドに対する読み書きはアトミックになるとは限らないためです。 そのような型のフィールドへのマルチスレッド アクセスを保護するために、Interlocked クラス メンバーを使用するか、lock ステートメントでアクセスを保護します。

volatile キーワードは class または struct のフィールドにのみ適用できます。 ローカル変数を volatile として宣言することはできません。

次の例は、public のフィールド変数を 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# の構文と使用法に関する信頼性のある情報源です。

関連項目