AutoResetEvent の完全な理解
AutoResetEvent は、マルチスレッド環境でスレッドの実行順序を調整するために使用されるスレッド同期プリミティブです。これは、スレッド間の通信とコラボレーションのために .NET Framework によって提供される同期メカニズムです。
AutoResetEvent は状態フラグを維持します。このフラグは、信号が送られるか送られないかの 2 つの状態のいずれかになります。スレッドは、シグナルを待機するかシグナルを設定することによって、自身の実行を制御できます。
AutoResetEvent には、WaitOne と Set という 2 つの主なメソッドがあります。これらのメソッドの機能と使用法は次のとおりです。
1. WaitOne:
- スレッドが WaitOne メソッドを呼び出すと、シグナルを受信するまで待機状態になります。
- AutoResetEvent が通知されていない状態の場合、WaitOne メソッドはスレッドの実行をブロックします。
- AutoResetEvent がシグナル状態にある場合、WaitOne メソッドはシグナルを消費し、AutoResetEvent を非シグナル状態に戻します。
- WaitOne のオーバーロードされたメソッドを通じてタイムアウト時間を指定でき、指定された時間が経過すると、スレッドはシグナルを待たずに実行を継続します。
2. Set:
- スレッドが Set メソッドを呼び出すと、AutoResetEvent の状態がシグナル状態に設定されます。
- スレッドがシグナルを待っている場合、スレッドは起動して実行を開始します。
- シグナルを待っているスレッドがない場合、Set メソッドを呼び出すと AutoResetEvent の状態もシグナル状態に設定されますが、それ以外の効果はありません。
- Set メソッドが複数回呼び出された場合でも、AutoResetEvent は消費されるまで 1 回だけシグナルが残ります。
AutoResetEvent は、ドアのラッチや信号機のように機能します。スレッドは、ラッチが開くのを待つ (WaitOne) ことによって自身の実行をブロックできますが、他のスレッドはラッチを開くように設定する (Set) ことによって待機中のスレッドをウェイクアップできます。
AutoResetEvent は、マルチスレッド プログラミング、特にスレッドの実行順序を制御する必要がある場合、またはスレッド間の連携を実現する必要がある場合に非常に役立ちます。これは、スレッドの操作を同期し、スレッドが望ましい順序で実行されることを保証するためのシンプルかつ効率的な方法を提供します。
例えば
2 つのスレッドがあり、1 つのスレッドがアイテムの生成を担当し、もう 1 つのスレッドがアイテムの消費を担当するとします。プロデューサがアイテムを生成した後にコンシューマがアイテムを消費できるようにするには、これら 2 つのスレッドを同期する必要があります。
この同期は、AutoResetEvent を使用して簡単に実現できます。簡単なサンプルコードを次に示します。
using System;
using System.Threading;
class ProgramesetEvent producerEvent = new AutoResetEvent(false);
static AutoResetEvent consumerEvent = new AutoResetEvent(true);
static int item = 0;
static void Main()
{
Thread p
{
static AutoRonsumerThread = new Thread(ConsumeItems);
producerroducerThread = new Thread(ProduceItems);
Thread cThread.Start();
consumerThread.Start();
producerThread.Join();
consumerThread.Join();
Console.WriteLine("Finished");
}
static void ProduceItems()
{
for (int i = 0; i < 5; i++)
{
consumerEvent.WaitOne(); // 等待消费者消费物品
// 生产物品
item = i;
Console.WriteLine("Produced item: " + item);
producerEvent.Set(); // 发送信号给消费者
}
}
static void ConsumeItems()
{
for (int i = 0; i < 5; i++)
{
producerEvent.WaitOne(); // 等待生产者生产物品
// 消费物品
Console.WriteLine("Consumed item: " + item);
consumerEvent.Set(); // 发送信号给生产者
}
}
}
この例では、現在生産または消費されているアイテムを表す共有 `item` 変数があります。`ProducerEvent` はプロデューサー スレッドの実行を制御し、`consumerEvent` はコンシューマ スレッドの実行を制御します。
コンシューマ スレッドがアイテムを消費するまで、プロデューサ スレッドは `consumerEvent.WaitOne()` を呼び出すことによって待機状態に入ります。次に、プロデューサー スレッドは新しいアイテムを生成し、それを `item` 変数に保存し、`ProducerEvent.Set()` を呼び出してシグナルをコンシューマー スレッドに送信します。
コンシューマ スレッドは、プロデューサ スレッドがアイテムを生成するまで、`ProducerEvent.WaitOne()` を呼び出すことによって待機状態に入ります。次に、コンシューマー スレッドは `item` 変数からアイテムを取得して消費し、`consumerEvent.Set()` を呼び出してプロデューサー スレッドにシグナルを送信します。
このようにして、プロデューサー スレッドとコンシューマー スレッドはアイテムを交互に生成および消費し、相互の同期を保つことができます。AutoResetEvent を使用すると、プロデューサー スレッドとコンシューマー スレッドの間で正しい順次実行が確保され、競合状態やデータの不整合が回避されます。
新しい AutoResetEvent によって渡されるパラメータは何に使用されますか?
AutoResetEvent のコンストラクターで渡されるパラメーターはブール値で、信号の初期状態を指定するために使用されます。このパラメータは「initialState」と呼ばれます。
- `initialState` が `true` の場合、AutoResetEvent の初期状態はシグナル状態です。つまり、スレッドは待機中にブロックされず、直接実行できます。このとき、「WaitOne」メソッドが呼び出されたときに、AutoResetEvent が unsigned 状態であれば、スレッドはシグナルを待たずにすぐに実行を継続します。
- `initialState` が `false` の場合、AutoResetEvent の初期状態は非シグナル状態です。つまり、スレッドは待機中にブロックされ、シグナルを受信するまで実行を続けることができません。このとき、WaitOne メソッドを呼び出した際に、AutoResetEvent が非シグナル状態の場合、Set メソッドを呼び出してシグナル状態に設定するまでスレッドはブロックされます。
初期信号状態は、実際のニーズに応じて選択できます。スレッドが最初にブロックされずに実行を継続したい場合は、シグナル状態を使用できます。スレッドが最初に実行するシグナルを待機するようにしたい場合は、シグナルなし状態を使用できます。
たとえば、プロデューサとコンシューマの例では、非シグナル状態を初期状態として使用して、プロデューサ スレッドが最初にコンシューマ スレッドからのシグナルを待ってアイテムの生成を開始できるようにすることができます。これにより、消費者が消費しないときに生産者が新しいアイテムを継続的に生産することができなくなります。
AutoResetEvent と ManualResetEvent の違いは何ですか
AutoResetEvent と ManualResetEvent は両方とも、マルチスレッド環境でスレッドの実行順序を調整するために使用されるスレッド同期プリミティブですが、それらの間にはいくつかの重要な違いがあります。
1. トリガーメソッド:
- AutoResetEvent: `Set` メソッドを呼び出した後、待機中のスレッドを 1 つだけ起動して実行を継続し、その後 AutoResetEvent は自動的に信号なし状態に戻ります。つまり、「Set」メソッドを呼び出すたびに、待機中のスレッドが 1 つだけ起動されます。
- ManualResetEvent: `Set` メソッドを呼び出した後、待機中のすべてのスレッドが起動され、明示的に `Reset` メソッドを呼び出して ManualResetEvent が非シグナル状態に戻るまで実行を続けます。つまり、「Set」メソッドを呼び出すたびに、待機中のすべてのスレッドが起動されます。
2. 状態リセット:
- AutoResetEvent: 無信号状態に自動的にリセットされます。つまり、スレッドがシグナルを待っていてウェイクアップされると、AutoResetEvent は自動的にシグナルなし状態にリセットされ、次に待機しているスレッドが引き続き待機できるようになります。
- ManualResetEvent: シグナルのない状態にリセットするには、「Reset」メソッドを明示的に呼び出す必要があります。つまり、ManualResetEvent は、「Reset」メソッドが呼び出されてシグナルなしの状態に戻るまでシグナル状態のままになります。
3. 待機メソッド:
- AutoResetEvent: `WaitOne` メソッドを呼び出すとき、AutoResetEvent がシグナルなし状態にある場合、スレッドはシグナルを受信するまでブロックされます。AutoResetEvent がシグナルされると、スレッドはシグナルを消費して実行を継続します。
- ManualResetEvent: `WaitOne` メソッドを呼び出すときに、ManualResetEvent がシグナルなし状態にある場合、スレッドはシグナルを受信するまでブロックされます。ManualResetEvent がシグナル状態にある場合でも、スレッドはシグナルを消費し、自動的に非シグナル状態にリセットせずに実行を継続します。
これらの違いに応じて、AutoResetEvent または ManualResetEvent の使用を選択して、さまざまなスレッド同期要件に適応できます。
- 待機中のスレッドを一度に 1 つだけウェイクアップする必要があり、シグナルの受信後にスレッドをシグナルなし状態に自動的にリセットしたい場合は、AutoResetEvent を使用できます。これは、タスクがキューに入れられる一部のシナリオで役立ちます。
- 待機中のすべてのスレッドをウェイクアップする必要があり、シグナルを受信した後、Reset メソッドを明示的に呼び出して非シグナル状態にリセットされるまでスレッドをシグナル状態のままにしておく必要がある場合は、ManualResetEvent を使用できます。これは、一部のブロードキャスト通知シナリオで役立ちます。
特定のアプリケーション シナリオとスレッド同期要件に応じて、どの同期プリミティブを使用するかを選択する必要があります。
まとめ
信号同期の原理は、信号を待ち、信号状態を直接通過し、信号がない場合は待つことです。何を待っていますか? WaitOnceなどを呼び出すことで Set は信号がある状態に設定し、ReSet は信号がない状態に設定します。AutoResetEvent は待機後に自動的に無信号になります。その反対は ManualResetEven です。
手動翻訳は手動です。シグナルのない状態にリセットするには、「Reset」メソッドを明示的に呼び出す必要があります。
もう 1 つの重要な問題は、複数のものの違いです。
同じ AutoResetEvent オブジェクトを通じて、異なるスレッドで WaitOnce を何度も呼び出すことができます。
このようにして、多くのスレッドが同じシグナルを待ちます。AutoResetEvent はシグナルを自動的に復元しないため、待機中のスレッドが 1 つだけ起動され、実行を継続します。その後、このスレッドは、WaitOnce を呼び出して渡されたため、最初に WaitOnce を呼び出す必要があります。 , AutoResetEvent は、信号がない状態を自動的に復元します。残りは当然待たなければなりません。
また、ManualResetEven は手動であるため、すべてをウェイクアップできます。