指定のコードブロックに対して、複数のスレッドが同時にアクセスできないように制限できます。ただし、同一のスレッドからのアクセスは制限されません。
lock (obj) statementlock ステートメント - C# リファレンス | Microsoft Learn
objは、同期が必要なリソースでロックのスコープを定義するために使用されます。複数のスレッドが同一のオブジェクトをロックすることによるデッドロックを防ぐため、publicなオブジェクトは避けます。また同期ブロック インデックスのメンバが必要なため、参照型とします。同期の対象がコレクションならば、それのSyncRootプロパティがこれの代用となります。
return this;
となっており、Array自身が返される。System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);
となっており、スレッドセーフに生成されたObjectが返される。このインスタンスはprivate Object _syncRoot;
としてprivateなクラスフィールドとして保持される。また以下のクラスのオブジェクトは、アプリケーション ドメインの境界を超えてアクセスされる恐れがあるため、利用を避けます。
これらのクラスから派生するクラスのオブジェクトをロック用のオブジェクトとすると、コード分析で「ID が不十分なオブジェクトをロックしないでください」としてCA2002で警告されます。
statementには複数のスレッドからのアクセスを認められない文を記述し、あるスレッドがこのstatementを抜けるまで、他のスレッドはこの直前で待機させられます。
lockでは無制限に待機するため、これを制限するタイムアウトを指定するにはMonitor.TryEnter()を用います。
たとえばあるオブジェクトを初期化する場合を考えます。
private readonly object lockObj = new object(); // ロック用のオブジェクト
if (target == null)
{
lock (lockObj)
{
// lockの直前に他のスレッドによって書き換えられていないか再確認する
if (target == null)
{
target = new MyClass();
}
}
}
lockステートメントはMonitorクラスのEnterとExitを呼ぶショートカットに過ぎず、
lock (x)
{
// 共有リソースへのアクセス
}
と記述することは、
Monitor.Enter(x); try { // 共有リソースへのアクセス } finally { Monitor.Exit(x); }
とすることと同義です。Thread Synchronization (C# and Visual Basic) | MSDN
ただしC# 4.0以降は、次のように変更されているようです。 c# - What does a lock statement do under the hood? - Stack Overflow lockWasTaken - Locks and exceptions do not mix | Fabulous adventures in coding
bool lockTaken = false; try { Monitor.Enter(x, ref lockTaken); // 共有リソースへのアクセス } finally { // ロックを得られていたならば、それを解放する if (lockTaken) Monitor.Exit(x); }lock ステートメント - C# リファレンス | Microsoft Learn
lockではロックが解放されるのを待って同期ブロックが順に実行されますが、1つのスレッドが実行すれば良いだけならば、次のようにMonitor.TryEnter()を用います。
if (Monitor.TryEnter(x)) { // ロックを取得できた try { // 共有リソースへのアクセス } finally { Monitor.Exit(x); } }
この場合ロックを取得したスレッドが共有リソースを処理し、他のスレッドは何もしません。
ロックは、それを取得したスレッドで解放しなければなりません。このような制約を受けたくないならば、SemaphoreSlimを用います。
object lockObj = new Object();
lock (lockObj)
{
await Task.Delay(10); // error CS1996: lock ステートメントの本体で待機することはできません。
}
他方がロックを取得しているリソースの解放を、それぞれが待つとデッドロックします。
object o1 = new object(); object o2 = new object(); Action action1 = delegate { lock (o1) { Task.Delay(10).Wait(); lock (o2) { } // o2の解放を待つが、o1をロックしているため解放されない } }; Action action2 = delegate { lock (o2) { Task.Delay(10).Wait(); lock (o1) { } // o1の解放を待つが、o2をロックしているため解放されない } }; Parallel.Invoke(action1, action2);第4回 デッドロックの回避とスレッド間での同期制御 ― マルチスレッド・プログラミングにおける排他制御と同期制御(後編) ―:連載.NETマルチスレッド・プログラミング入門(1/3 ページ) - @IT 高木健一 (2005/06/15)