.NET には、共有リソースへのアクセスの同期やスレッド操作の調整に使用できるさまざまな種類が用意されています。
Von Bedeutung
共有リソースのアクセスを保護するには、同じ同期プリミティブ インスタンスを使用します。 異なる同期プリミティブ インスタンスを使用して同じリソースを保護する場合は、同期プリミティブによって提供される保護を回避します。
WaitHandle クラスと軽量同期の種類
複数の .NET 同期プリミティブは、ネイティブ オペレーティング システムの同期ハンドルをカプセル化し、スレッドの相互作用にシグナリング メカニズムを使用する System.Threading.WaitHandle クラスから派生します。 これらのクラスには、次のものが含まれます。
- System.Threading.Mutex: 共有リソースへの排他的アクセスを許可します。 ミューテックスの状態は、所有しているスレッドがない場合に通知されます。
- System.Threading.Semaphoreは、共有リソースまたはリソースのプールに同時にアクセスできるスレッドの数を制限します。 セマフォの状態は、カウントが 0 より大きい場合はシグナルに設定され、カウントが 0 の場合は非割り当てに設定されます。
- System.Threading.EventWaitHandleは、スレッド同期イベントを表し、シグナル状態または符号なし状態にすることができます。
- System.Threading.AutoResetEventは、 EventWaitHandle から派生し、通知されると、1 つの待機中のスレッドを解放した後、自動的に署名されていない状態にリセットされます。
- System.Threading.ManualResetEventは、 EventWaitHandle から派生し、シグナル通知されると、 Reset メソッドが呼び出されるまでシグナル状態のままです。
.NET Framework では、 WaitHandle は System.MarshalByRefObjectから派生しているため、これらの型を使用して、アプリケーション ドメインの境界を越えてスレッドのアクティビティを同期できます。
.NET Framework、.NET Core、および .NET 5 以降では、これらの型の一部は、オペレーティング システム全体で表示され、プロセス間の同期に使用できる名前付きシステム同期ハンドルを表すことができます。
- Mutex
- Semaphore (Windows の場合)
- EventWaitHandle (Windows の場合)
詳細については、 WaitHandle API リファレンスを参照してください。
軽量の同期の種類は、基になるオペレーティング システム ハンドルに依存せず、通常はパフォーマンスが向上します。 ただし、プロセス間同期には使用できません。 これらの型は、1 つのアプリケーション内でのスレッド同期に使用します。
これらの型の一部は、 WaitHandleから派生した型の代替手段です。 たとえば、 SemaphoreSlim は、 Semaphoreに代わる軽量の代替手段です。
共有リソースへのアクセスの同期
.NET には、複数のスレッドによる共有リソースへのアクセスを制御するためのさまざまな同期プリミティブが用意されています。
Monitor クラス
System.Threading.Monitor クラスは、リソースを識別するオブジェクトのロックを取得または解放することで、共有リソースへの相互排他的アクセスを許可します。 ロックが保持されている間、ロックを保持しているスレッドはロックを再度取得して解放できます。 その他のスレッドはロックの取得をブロックされ、 Monitor.Enter メソッドはロックが解放されるまで待機します。 Enter メソッドは、解放されたロックを取得します。 Monitor.TryEnter メソッドを使用して、スレッドがロックを取得しようとする時間を指定することもできます。 Monitor クラスにはスレッド アフィニティがあるため、ロックを取得したスレッドは、Monitor.Exit メソッドを呼び出してロックを解放する必要があります。
Monitor.Wait、Monitor.Pulse、およびMonitor.PulseAllメソッドを使用して、同じオブジェクトに対するロックを取得するスレッドの相互作用を調整できます。
詳細については、 Monitor API リファレンスを参照してください。
注
Monitor クラスを直接使用するのではなく、C# の lock ステートメントと Visual Basic の SyncLock ステートメントを使用して、共有リソースへのアクセスを同期します。 これらのステートメントは、取得したロックが常に解放されるように、 Enter メソッドと Exit メソッドと try…finally
ブロックを使用して実装されます。
Mutex クラス
System.Threading.Mutex クラスは、Monitorと同様に、共有リソースへの排他的アクセスを許可します。 Mutex.WaitOne メソッドのオーバーロードのいずれかを使用して、ミューテックスの所有権を要求します。 Monitorと同様に、Mutexにはスレッド アフィニティがあり、ミューテックスを取得したスレッドは、Mutex.ReleaseMutex メソッドを呼び出して解放する必要があります。
Monitorとは異なり、Mutex クラスはプロセス間同期に使用できます。 これを行うには、オペレーティング システム全体で表示される名前付きミューテックスを使用します。 名前付きミューテックス インスタンスを作成するには、名前を指定する Mutex コンストラクター を使用します。 Mutex.OpenExisting メソッドを呼び出して、既存の名前付きシステム ミューテックスを開くこともできます。
詳細については、 ミューテックスに関する 記事と Mutex API リファレンスを参照してください。
SpinLock 構造体
System.Threading.SpinLock構造体は、Monitorと同様に、ロックの可用性に基づいて共有リソースへの排他的アクセスを許可します。 使用できないロック SpinLock 取得しようとすると、ループ内で待機し、ロックが使用可能になるまで繰り返しチェックします。
スピン ロックを使用する利点と欠点の詳細については、 SpinLock の記事と SpinLock API リファレンスを参照してください。
ReaderWriterLockSlim クラス
System.Threading.ReaderWriterLockSlim クラスは、書き込み用の共有リソースへの排他的アクセスを許可し、複数のスレッドが読み取りのためにリソースに同時にアクセスできるようにします。 ReaderWriterLockSlimを使用して、スレッド セーフな読み取り操作をサポートするが、書き込み操作を実行するには排他アクセスが必要な共有データ構造へのアクセスを同期できます。 スレッドが (たとえば、 ReaderWriterLockSlim.EnterWriteLock メソッドを呼び出すことによって) 排他アクセスを要求すると、後続のリーダーとライターは、既存のすべてのリーダーがロックを終了し、ライターがロックに入って終了するまでブロックします。
詳細については、 ReaderWriterLockSlim API リファレンスを参照してください。
Semaphore クラスと SemaphoreSlim クラス
System.Threading.SemaphoreクラスとSystem.Threading.SemaphoreSlim クラスでは、共有リソースまたはリソースのプールに同時にアクセスできるスレッドの数が制限されます。 リソースを要求する追加のスレッドは、任意のスレッドがセマフォを解放するまで待機します。 セマフォにはスレッド アフィニティがないため、スレッドはセマフォを取得でき、別のスレッドはセマフォを解放できます。
SemaphoreSlim は、 Semaphore の軽量な代替手段であり、1 つのプロセス境界内での同期にのみ使用できます。
Windows では、プロセス間同期に Semaphore を使用できます。 これを行うには、名前またはSemaphore.OpenExisting メソッドを指定するセマフォ コンストラクターのいずれかを使用して、名前付きシステム セマフォを表すSemaphore インスタンスを作成します。 SemaphoreSlim では、名前付きシステム セマフォはサポートされていません。
詳細については、 Semaphore および SemaphoreSlim に関する記事と、 Semaphore または SemaphoreSlim API リファレンスを参照してください。
スレッドの相互作用、またはシグナル通知
スレッドの相互作用 (またはスレッド 通知) とは、スレッドが続行するために、1 つ以上のスレッドからの通知またはシグナルを待機する必要があることを意味します。 たとえば、スレッド A がスレッド B の Thread.Join メソッドを呼び出した場合、スレッド A はスレッド B が完了するまでブロックされます。 前のセクションで説明した同期プリミティブは、通知のための別のメカニズムを提供します。ロックを解放することで、スレッドはロックを取得することで続行できることを別のスレッドに通知します。
このセクションでは、.NET によって提供される追加のシグナル構成体について説明します。
EventWaitHandle、AutoResetEvent、ManualResetEvent、ManualResetEventSlim クラス
System.Threading.EventWaitHandle クラスは、スレッド同期イベントを表します。
同期イベントは、署名されていない状態でもシグナル状態でもかまいません。 イベントの状態が署名されていない場合、イベントの WaitOne オーバーロードを呼び出すスレッドは、イベントが通知されるまでブロックされます。 EventWaitHandle.Set メソッドは、イベントの状態を signaled に設定します。
通知された EventWaitHandle の動作は、リセット モードによって異なります。
- EventResetMode.AutoReset フラグを使用して作成されたEventWaitHandleは、1 つの待機中のスレッドを解放した後に自動的にリセットされます。 これは、通知されるたびに 1 つのスレッドのみを許可するターンタイルのようなものです。 EventWaitHandleから派生した System.Threading.AutoResetEvent クラスは、その動作を表します。
- EventResetMode.ManualReset フラグを使用して作成されたEventWaitHandleは、Reset メソッドが呼び出されるまでシグナル通知されます。 これは、通知されるまで閉じられ、誰かが閉じるまで開いたままになっているゲートのようなものです。 EventWaitHandleから派生した System.Threading.ManualResetEvent クラスは、その動作を表します。 System.Threading.ManualResetEventSlim クラスは、ManualResetEventに代わる軽量の代替手段です。
Windows では、プロセス間同期に EventWaitHandle を使用できます。 そのためには、名前またはEventWaitHandle.OpenExisting メソッドを指定する EventWaitHandle コンストラクターのいずれかを使用して、名前付きシステム同期イベントを表すEventWaitHandle インスタンスを作成します。
詳細については、 EventWaitHandle の記事を参照してください。 API リファレンスについては、 EventWaitHandle、 AutoResetEvent、 ManualResetEvent、 ManualResetEventSlimを参照してください。
CountdownEvent クラス
System.Threading.CountdownEvent クラスは、カウントが 0 のときに設定されるイベントを表します。 CountdownEvent.CurrentCountが 0 より大きい場合、CountdownEvent.Waitを呼び出すスレッドはブロックされます。 イベントの数を減らすために CountdownEvent.Signal を呼び出します。
1 つのスレッドからのシグナルを使用して複数のスレッドのブロックを解除できる ManualResetEvent や ManualResetEventSlimとは異なり、 CountdownEvent を使用して、複数のスレッドからのシグナルを含む 1 つ以上のスレッドのブロックを解除できます。
詳細については、 CountdownEvent の記事と CountdownEvent API リファレンスを参照してください。
Barrier クラス
System.Threading.Barrier クラスは、スレッド実行バリアを表します。 Barrier.SignalAndWait メソッドを呼び出すスレッドは、バリアに到達したことを通知し、他の参加者スレッドがバリアに到達するまで待機します。 すべての参加スレッドがバリアに到達すると、続行され、バリアがリセットされ、再び使用できます。
次の計算フェーズに進む前に、1 つ以上のスレッドが他のスレッドの結果を必要とする場合は、 Barrier を使用できます。
詳細については、 Barrier に関する記事と Barrier API リファレンスを参照してください。
Interlocked クラス
System.Threading.Interlocked クラスは、変数に対して単純なアトミック操作を実行する静的メソッドを提供します。 これらのアトミック操作には、加算、インクリメントとデクリメント、比較に依存する交換と条件付き交換、64 ビット整数値の読み取り操作が含まれます。
詳細については、 Interlocked API リファレンスを参照してください。
SpinWait 構造体
System.Threading.SpinWait構造体は、スピンベースの待機をサポートします。 スレッドがイベントが通知されるのを待つ必要がある場合や条件が満たされる場合に、実際の待機時間が待機ハンドルを使用するか、スレッドをブロックすることによって必要な待機時間よりも短いと予想される場合に使用できます。 SpinWaitを使用すると、待機中にスピンする短い期間を指定し、指定した時間内に条件が満たされなかった場合にのみ (たとえば、待機またはスリープ状態で) 生成できます。
詳細については、 SpinWait の記事と SpinWait API リファレンスを参照してください。
こちらも参照ください
.NET