私はこれに関する文書を読みました、そして私は理解していると思います。コードがevent.WaitOne()
を通過すると、 AutoResetEvent
はリセットされますが、 ManualResetEvent
はリセットされません。
これは正しいです?
はい。料金所とドアの違いのようです。 ManualResetEvent
はドアであり、手動で閉じる(リセットする)必要があります。 AutoResetEvent
は料金所で、1台の車が通り過ぎて次の車が通過する前に自動的に閉じます。
AutoResetEvent
がWaitOne()
とReset()
を単一のアトミック操作として実行すると想像してください。
短い答えはイエスです。最も重要な違いは、AutoResetEventは1つの待機中のスレッドだけが続行を許可することです。一方、ManualResetEventは、同時に停止するように指示するまで、いくつかのスレッドが同時に続行できるようにします。
Joseph AlbahariによるC#3.0の一言書からの抜粋
ManualResetEventはAutoResetEventの一種です。異なるのは、スレッドがWaitOne呼び出しを通過した後に自動的にリセットされないため、ゲートのように機能するということです。 Resetを呼び出すとゲートが閉じられるため、次のゲートが開くまでウェイターのキューが累積する可能性があります。
フラグを繰り返しチェックしてから短期間スリープ状態にする - 「スピンスリープ」と組み合わせたブール型の「gateOpen」フィールド(volatileキーワードで宣言)を使用してこの機能をシミュレートすることもできます。
ManualResetEventsは、特定の操作が完了したこと、またはスレッドの初期化が完了して作業を実行する準備ができたことを知らせるために使用されることがあります。
ManualResetEvent
とAutoResetEvent
の理解を明確にするために、簡単な例を作成しました。
AutoResetEvent
:3人のワーカースレッドがあるとしましょう。これらのスレッドのいずれかがWaitOne()
を呼び出すと、他の2つすべてのスレッドが実行を停止してシグナルを待ちます。私は彼らがWaitOne()
を使っていると思います。のようなものです。私が働かなければ、だれも働かない。最初の例では、あなたはそれを見ることができます
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Set()
を呼び出すと、すべてのスレッドが機能してシグナルを待ちます。 1秒後に私は2番目のシグナルを送信しています、そしてそれらは実行されそして待ちます(WaitOne()
)。これらの人たちがサッカーチームの選手であり、ある選手が上司が私に電話するまで待つことを言い、他の人が上司が彼らに続けるように指示するまで待つことを考える(Set()
)
public class AutoResetEventSample
{
private AutoResetEvent autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
}
この例では、最初にSet()
を押すとすべてのスレッドが解放され、1秒後にはすべてのスレッドが待機するように通知されます。内部でWaitOne()
を呼び出しているにもかかわらず、それらを再び設定するとすぐに、それらをすべて停止するには手動でReset()
を呼び出す必要があるため、これらは実行されたままになります。
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
それはプレーヤーのいずれかが怪我をしていても関係なく審判員/プレーヤーの関係についてであり、他の人が遊ぶのを待つのは仕事を続けるでしょう。審判がwait(Reset()
)と言った場合、全プレーヤーは次の合図まで待つでしょう。
public class ManualResetEventSample
{
private ManualResetEvent manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
}
autoResetEvent.WaitOne()
と類似しています
try
{
manualResetEvent.WaitOne();
}
finally
{
manualResetEvent.Reset();
}
アトミック操作として
通常、同じスレッドに2つの回答を追加することはお勧めできませんが、以前の回答を編集/削除したくはありませんでした。これは別の方法で役立つ可能性があるためです。
さて、私は、もっと包括的で理解しやすい、すぐに使えるコンソールアプリスニペットを以下に作成しました。
例を2つの異なるコンソールで実行して、動作を観察してください。舞台裏で何が起こっているのか、もっと明確なアイデアを得るでしょう。
手動リセットイベント
using System;
using System.Threading;
namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
public class ManualResetEventSample
{
private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
Thread.Sleep(15000);
Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Reset();
Thread.Sleep(2000);
Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
Thread.Sleep(10000);
Console.WriteLine();
Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
Thread.Sleep(5000);
Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker1()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker2()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker3()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
}
}
自動リセットイベント
using System;
using System.Threading;
namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
public class AutoResetEventSample
{
private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
Thread.Sleep(15000);
Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Reset();
Thread.Sleep(2000);
Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
Thread.Sleep(10000);
Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
Thread.Sleep(5000);
Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker1()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker2()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker3()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
}
}
はい。これは絶対に正しいです。
ManualResetEventは、状態を示す方法として見ることができます。何かがオン(セット)またはオフ(リセット)です。ある程度の期間のある出来事。その状態が起こるのを待っているどのスレッドでも続行できます。
AutoResetEventは、シグナルとよりよく似ています。何かが起こったというワンショットの兆候。期間のない出来事。必ずというわけではありませんが、通常、発生した「何か」は小さく、シングルスレッドで処理する必要があります。したがって、シングルスレッドがイベントを消費した後の自動リセットです。
AutoResetEventブール変数をメモリに保持します。ブール変数がfalseの場合はスレッドをブロックし、ブール変数がtrueの場合はスレッドのブロックを解除します。
AutoResetEventオブジェクトをインスタンス化するとき、コンストラクタにデフォルト値のboolean値を渡します。以下は、AutoResetEventオブジェクトをインスタンス化する構文です。
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
WaitOneメソッド
このメソッドは現在のスレッドをブロックし、他のスレッドによるシグナルを待ちます。 WaitOneメソッドは、現在のスレッドをスリープスレッド状態にします。 WaitOneメソッドは、シグナルを受信した場合はtrueを返し、それ以外の場合はfalseを返します。
autoResetEvent.WaitOne();
WaitOneメソッドの2番目のオーバーロードは、指定された秒数の間待機します。シグナルが得られない場合は、スレッドは作業を続けます。
static void ThreadMethod()
{
while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
{
Console.WriteLine("Continue");
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Console.WriteLine("Thread got signal");
}
引数として2秒を渡してWaitOneメソッドを呼び出しました。 whileループでは、シグナルを2秒間待ってから作業を続けます。スレッドがシグナルを受け取ると、WaitOneはtrueを返してループを終了し、「Thread got signal」を出力します。
設定方法
AutoResetEvent Setメソッドは、待機中のスレッドにシグナルを送信して作業を進めます。以下は、Setメソッドを呼び出す構文です。
autoResetEvent.Set();
ManualResetEventブール変数をメモリに保持します。ブール変数がfalseの場合はすべてのスレッドをブロックし、ブール変数がtrueの場合はすべてのスレッドのブロックを解除します。
ManualResetEventをインスタンス化するときに、デフォルトのブール値で初期化します。
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
上記のコードでは、ManualResetEventをfalse値で初期化します。つまり、WaitOneメソッドを呼び出すすべてのスレッドは、あるスレッドがSet()メソッドを呼び出すまでブロックされます。
ManualResetEventをtrueの値で初期化した場合、WaitOneメソッドを呼び出すすべてのスレッドがブロックされることはなく、先に進むことができます。
WaitOneメソッド
このメソッドは現在のスレッドをブロックし、他のスレッドによるシグナルを待ちます。シグナルを受信した場合はtrueを返し、それ以外の場合はfalseを返します。
以下は、WaitOneメソッドを呼び出す構文です。
manualResetEvent.WaitOne();
WaitOneメソッドの2番目のオーバーロードでは、現在のスレッドがシグナルを待つまでの時間間隔を指定できます。時間内であれば、シグナルを受け取らずにfalseを返し、メソッドの次の行に進みます。
以下は、時間間隔でWaitOneメソッドを呼び出す構文です。
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
WaitOneメソッドに5秒を指定しました。 manualResetEventオブジェクトが5秒以内にシグナルを受信しない場合、isSignalled変数をfalseに設定します。
設定方法
このメソッドは、待機中のすべてのスレッドにシグナルを送信するために使用されます。 Set()メソッドは、ManualResetEventオブジェクトのブール変数をtrueに設定します。待機中のスレッドはすべてブロック解除され、さらに先へ進みます。
以下は、Set()メソッドを呼び出す構文です。
manualResetEvent.Set();
リセット方法
ManualResetEventオブジェクトでSet()メソッドを呼び出すと、そのブール値はtrueのままです。値をリセットするには、Reset()メソッドを使用します。 resetメソッドはブール値をfalseに変更します。
以下は、Resetメソッドを呼び出す構文です。
manualResetEvent.Reset();
シグナルを複数回スレッドに送信したい場合は、Setメソッドを呼び出した後すぐにResetメソッドを呼び出す必要があります。
はい、そうです。
あなたはこれら二つの用法によって考えを得ることができます。
作業が終了し、これを待っている他のスレッドが続行できることを伝える必要がある場合は、ManualResetEventを使用する必要があります。
リソースに相互に排他的にアクセスする必要がある場合は、AutoResetEventを使用してください。
AutoResetEventとManualResetEventを理解したい場合は、スレッド化ではなく割り込みを理解する必要があります。
.NETは低レベルのプログラミングを可能な限り遠く離れたところで起動しようとしています。
割り込みは、低レベルプログラミングで使用されるもので、低レベルから高レベル(またはその逆)になったことを示すシグナルに相当します。この場合、プログラムは通常の実行を中断し、実行ポインタをこのイベントを処理する関数に移動します。
ハードウェアが次のように動作するため、割り込みが発生したときに最初にすべきことは、リセットその状態です。
これがManualResetEventとAutoResetEventの違いです。
ManualResetEventが発生してリセットしないと、次回発生したときにリスニングできません