質問

これに関するドキュメントを読み、理解していると思います。アン AutoResetEvent コードが通過するとリセットされる event.WaitOne(), 、しかし、 ManualResetEvent ではない。

これは正しいです?

役に立ちましたか?

解決

はい。それは料金所とドアの違いのようなものです。 ManualResetEvent はドアであり、手動で閉じる(リセットする)必要があります。 AutoResetEvent は料金所であり、1台の車が通過し、次の車が通過する前に自動的に閉じることができます。

他のヒント

AutoResetEvent WaitOne()および Reset()を単一のアトミック操作として実行すると想像してください。

短い答えはイエスです。最も重要な違いは、AutoResetEventが1つの待機スレッドのみを続行できることです。一方、ManualResetEventは、停止する(リセットする)ように指示するまで、複数のスレッドを同時に許可し続けます。

C#3.0 Nutshell Book、JosephAlbahariに撮影

C# でのスレッド化 - 無料の電子書籍

ManualResetEvent は AutoResetEvent のバリエーションです。WaitOne 呼び出しでスレッドが通過した後に自動的にリセットされない点が異なり、ゲートのように機能します。Set を呼び出すとゲートが開き、ゲートで WaitOne が通過する任意の数のスレッドが許可されます。Reset を呼び出すとゲートが閉じられ、次にゲートが開くまで待機者のキューが蓄積される可能性があります。

この機能は、ブール値の「gateOpen」フィールド (volatile キーワードで宣言) と「スピン スリープ」 (フラグを繰り返しチェックしてから短期間スリープする) を組み合わせてシミュレートできます。

ManualResetEvent は、特定の操作が完了したこと、またはスレッドの初期化が完了して作業を実行する準備ができたことを通知するために使用されることがあります。

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();

負傷しているプレーヤーの有無に関係なく、レフリー/プレーヤーの関係について詳しく説明します。レフリーが待機( 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();
}

アトミック操作として

OK、通常は同じスレッドに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は、信号にさらに匹敵します。何かが起こったことを一発で示す。期間のないオカレンス。通常、「ではなく」何かとは限りません。発生したことは小さく、単一のスレッドで処理する必要があります。したがって、単一のスレッドがイベントを消費した後の自動リセットです。

はい、そうです。

これらの2つの使用法によってアイデアを得ることができます。

いくつかの作業が終了し、これを待っている他の(スレッド)が続行できることを通知する必要がある場合は、ManualResetEventを使用する必要があります。

リソースへの相互排他アクセスが必要な場合は、AutoResetEventを使用する必要があります。

AutoResetEvent は、ブール変数をメモリに保持します。ブール変数がfalseの場合、スレッドをブロックし、ブール変数がtrueの場合、スレッドのブロックを解除します。

AutoResetEventオブジェクトをインスタンス化するとき、ブール値のデフォルト値をコンストラクターに渡します。 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を返し、ループを終了して「スレッドがシグナルを取得しました」を出力します。

Setメソッド

AutoResetEvent Setメソッドは、作業を続行するために待機スレッドにシグナルを送信しました。以下は、Setメソッドを呼び出す構文です。

autoResetEvent.Set();

ManualResetEvent は、ブール変数をメモリに保持します。ブール変数がfalseの場合、すべてのスレッドをブロックし、ブール変数がtrueの場合、すべてのスレッドのブロックを解除します。

ManualResetEventをインスタンス化するとき、デフォルトのブール値で初期化します。

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

上記のコードでは、ManualResetEventをfalse値で初期化します。つまり、一部のスレッドがSet()メソッドを呼び出すまで、WaitOneメソッドを呼び出すすべてのスレッドがブロックされます。

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メソッド

このメソッドは、すべての待機スレッドにシグナルを送信するために使用されます。 Set()メソッドは、ManualResetEventオブジェクトのブール変数をtrueに設定します。待機中のスレッドはすべてブロック解除され、先に進みます。

以下はSet()メソッドを呼び出す構文です。

manualResetEvent.Set();

リセット方法

ManualResetEventオブジェクトでSet()メソッドを呼び出すと、そのブール値はtrueのままです。値をリセットするには、Reset()メソッドを使用できます。 Resetメソッドはブール値をfalseに変更します。

以下はResetメソッドを呼び出す構文です。

manualResetEvent.Reset();

スレッドに複数回シグナルを送信する場合は、Setメソッドを呼び出した後、すぐにResetメソッドを呼び出す必要があります。

AutoResetEventとManualResetEventを理解したい場合、スレッドではなく割り込みを理解する必要があります!

.NETは、可能な限り最も低レベルのプログラミングを思い起こさせたいと考えています。

割り込みは、低レベルのプログラミングで使用されるもので、低から高になった(またはその逆)信号に相当します。これが発生すると、プログラムは通常の実行を中断し、実行ポインターをこのイベントを処理する関数に移動します。

割り込みが発生したときに最初に行うことは、ハードウェアが次のように動作するため、その状態をリセットすることです。

  1. ピンが信号に接続され、ハードウェアが信号の変化をリッスンします(信号には2つの状態しかありません)。
  2. 信号が変化した場合、何かが発生し、ハードウェアが発生した状態にメモリ変数を設定します(信号が再び変化してもこの状態のままです)。
  3. プログラムは、変数が状態を変更し、実行を処理関数に移動することに気付きます。
  4. ここで最初にすべきことは、この割り込みを再度リッスンできるようにするために、このメモリ変数を未発生状態にリセットすることです。

これは、ManualResetEventとAutoResetEventの違いです。
ManualResetEventが発生し、それをリセットしないと、次回発生したときにそれを聞くことができなくなります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top