変数をスレッドセーフに更新できます。
| メソッド | 機能 |
|---|---|
| Increment() | 不可分操作として、変数を増加できる |
| Decrement() | 不可分操作として、変数を減少できる |
| Add() | 不可分操作として、2つの整数を加算できる |
| Exchange() | 不可分操作として、変数を設定できる |
| CompareExchange() | 2つの値が等しいとき、最初の値を置換できる
条件を満たしたときのみ置換できるため、たとえば初期値と比較することで初期化がくりかえされないようにできる |
| Read() | 不可分操作として、64bit値を読み込める |
| MemoryBarrier() | メモリのアクセスを同期できる。これはThread.MemoryBarrier()のラッパーであり、内部ではこのメソッドを呼び出すに過ぎない。MemoryBarrier - interlocked.cs |
不可分操作 (atomic operation) として、変数を増加 (インクリメント) できます。
public static int Increment (ref int location);Increment(Int32) - Interlocked.Increment Method (System.Threading) | Microsoft Learn
locationがインクリメントされ、そのインクリメントされた値が返されます。locationはこの処理の後に他のスレッドによって書き換えられる恐れがあるため、増加させた時点での値が必要ならば、返された値を用います。
int num = 0;
Action action = () =>
{
for (int i = 0; i < 10000; i++)
{
int result = Interlocked.Increment(ref num);
// この時点で、numは他のスレッドによって書き換えられている可能性がある
}
};
Parallel.Invoke(action, action);
この例では10000回のループを2回くり返しているため、numは最終的に20000となります。しかしnum++でインクリメントすると、そうならないことがあります。
スレッドセーフとするにはlockを用いて、
lock (lockObject) num++;
とも記述できますが、このメソッドを用いて
Interlocked.Increment(ref num);
とした方がパフォーマンスを向上できます。一般的な推奨事項 - マネージド スレッド処理のベスト プラクティス | Microsoft Learn
不可分操作として、変数を減少 (デクリメント) できます。
public static int Decrement (ref int location);Decrement(Int32) - Interlocked.Decrement Method (System.Threading) | Microsoft Learn
ConcurrentQueueでは、TryDequeue()の内部処理で用いられています。TryRemove - ConcurrentQueue.cs
不可分操作 (atomic operation) として、2つの32bit整数を加算できます。
public static int Add (
ref int location1,
int value
);
Add(Int32, Int32) - Interlocked.Add Method (System.Threading) | Microsoft Learn
2つの値の合計がlocation1に格納され、それが返されます。
不可分操作 (atomic operation) として、変数を設定できます。
public static T Exchange<T> (
ref T location1, // 設定する変数
T value // 設定する値
) where T : class;
Exchange<T>(T, T) - Interlocked.Exchange Method (System.Threading) | Microsoft Learn
location1がvalueに置き換えられ、設定する前の値が返されます。
int a1 = 1;
int a2 = Interlocked.Exchange(ref a1, 2);
// a1は2となり、1が返される
このメソッドは参照型にだけ対応します。値型にはInt32、Int64、IntPtr、SingleとDoubleに対応するオーバーロードが用意されていますが、これら以外には対応しません。Exchange<T>(T, T) - Interlocked.Exchange Method (System.Threading) | Microsoft Learn
非対応の値型は、Int32に置き換えることで対応できます。.net - How to apply InterLocked.Exchange for Enum Types in C#? - Stack Overflow
プロパティはrefで渡せない (CS0206) ため、それに設定することはできません。Errors and warnings associated with reference parameter modifiers - C# | Microsoft Learn
2つの値が等しいとき、最初の値を2番目の値に交換 (exchange) できます。比較と交換は、不可分操作として実行されます。
public static T CompareExchange<T> (
ref T location1, //
T value, // location1を置き換える値
T comparand // location1と比較する値
) where T : class;
CompareExchange<T>(T, T, T) - Interlocked.CompareExchange Method (System.Threading) | Microsoft Learn
location1がcomparandと等しかったならば、location1がvalueに置き換えられます。そして置き換える前のlocation1が返されます。
これは異なるスレッドからlocation1が同時に書き換えられるのを防ぐために用いられるメソッドであり、valueが同時に評価されることはあります。
object obj = null; Interlocked.CompareExchange(ref obj, Init(), null); // Init()が評価され、objはnullと等しいため、Init()の戻り値に置き換えられる Interlocked.CompareExchange(ref obj, Init(), null); // Init()が評価され、objはnullと等しくないため、置き換えられない
このメソッドで値型を使用しないようにします。オブジェクトは参照が等しいかで比較されるため、値型のインスタンスは同値であっても等しくないと評価され、常に交換されません。
変数がnullのときにそれに割り当てる処理、
if (obj == null)
{
lock (lockObject)
{
if (obj == null) obj = new MyClass();
}
}
は、このメソッドを用いると次のように記述できます。一般的な推奨事項 - マネージド スレッド処理のベスト プラクティス | Microsoft Learn
if (obj == null)
{
Interlocked.CompareExchange(ref obj, new MyClass(), null);
}
このときobjへの割り当ては1度しか行われませんが、複数のスレッドから同時にアクセスされたときに、new MyClass()が複数回呼ばれることはあります。
public static long Read (ref long location);Read(Int64) - Interlocked.Read Method (System.Threading) | Microsoft Learn
64bitシステムでは64bitの読み取りは不可分操作のため、このメソッドは不要です。32bitではこのメソッドを用いないと、不可分操作とはなりません。