volatile
キーワードは、同時に実行されている複数のスレッドによってフィールドが変更される可能性があることを示します。 パフォーマンス上の理由から、コンパイラ、ランタイム システム、さらにはハードウェアによって、メモリの場所への読み取りと書き込みが再配置される場合があります。
volatile
宣言されているフィールドは、特定の種類の最適化から除外されます。 すべての実行スレッドから見た揮発性書き込みの 1 つの合計順序は保証されません。 詳細については、Volatile クラスを参照してください。
注意事項
多くの場合、 volatile
キーワードは誤解され、マルチスレッド プログラミングで誤用されます。 ほとんどのシナリオでは、 volatile
ではなく、より安全で信頼性の高い代替手段を使用する必要があります。 最新の .NET には、 Interlocked クラス、 lock
ステートメント、上位レベルの同期プリミティブなどのコンカレンシー ツールが用意されています。 これらの代替手段は、 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
ステートメントを使用してアクセスを保護します。
ほとんどのマルチスレッド シナリオでは、サポートされている型であっても、Interlockedではなく、lock
操作、volatile
ステートメント、またはその他の同期プリミティブを使用することをお勧めします。 これらの代替手段は、微妙なコンカレンシーのバグが発生しにくくなります。
volatile
キーワードは、class
またはstruct
のフィールドにのみ適用できます。 ローカル変数は volatile
宣言できません。
揮発性の代替手段
ほとんどの場合、 volatile
ではなく、次のいずれかの安全な代替手段を使用する必要があります。
-
Interlocked 操作: 数値型と参照代入にアトミック操作を提供します。 これらは一般に高速であり、
volatile
よりも強力な保証を提供します。 -
lock
ステートメント: 相互の除外とメモリ バリアを提供します。 より大きなクリティカル セクションを保護するために使用します。 -
Volatile クラス: 明示的な揮発性の読み取りおよび書き込み操作を、
volatile
キーワードよりも明確なセマンティクスで提供します。 - 上位レベルの同期プリミティブ: ReaderWriterLockSlim、 Semaphore、 System.Collections.Concurrentからの同時実行コレクションなど。
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# の構文と使用法の決定的なソースです。
こちらも参照ください
.NET