c# - thread - task waitone




.NETのManualResetEventとAutoResetEventの違いは何ですか? (7)

Joseph Albahari著、C#3.0 Nutshellの本から引用

C#でスレッド化 - 無料の電子ブック

ManualResetEventは、AutoResetEventのバリ​​エーションです。 これは、スレッドがWaitOne呼び出しで通過した後に自動的にリセットされないという点で異なります。ゲートのように機能します。呼び出しセットはゲートを開き、ゲートでWaitOneの任意の数のスレッドを許可します。 呼び出し元リセットはゲートを閉じ、潜在的に待ち行列の待ち行列を次に開くまで蓄積させる。

"spin-sleeping"と組み合わせてブール型の "gateOpen"フィールド(volatileキーワードで宣言)を使ってこの機能をシミュレートすることができます。フラグを繰り返しチェックし、短時間スリープします。

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

私はこれについてのドキュメントを読んで、私は理解していると思う。 コードがevent.WaitOne()を通過するときにAutoResetEventリセットされますが、 ManualResetEventManualResetEventません。

これは正しいです?


ManualResetEventとAutoResetEventの理解を明確にするための簡単なサンプルを作成しました。

AutoResetEvent:3つのワーカースレッドがあると仮定します。 これらのスレッドのいずれかがWaitOne()を呼び出すと、他の2つのスレッドはすべて実行を停止し、シグナルを待機します。 私は彼らがWaitOne()を使用していると仮定しています。 それはのようです。 私が働かないと、誰も働かない。 最初の例では、

            autoReset.Set();
            Thread.Sleep(1000);
            autoReset.Set();

Set()を呼び出すと、 すべてのスレッドが動作し、信号を待機します。 1秒後に2番目の信号を送信し、実行して待機します(WaitOne();)。 これらの人はサッカーチームの選手だと思っています。もし1人のプレイヤーが私がマネージャーが私に電話するまで待つと言うと、他の人はマネージャーが彼らに続けるよう指示するまで待つでしょう(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();
}

原子操作として


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

AutoResetEventオブジェクトをインスタンス化すると、デフォルト値のboolean valueがコンストラクタに渡されます。 以下は、AutoResetEventオブジェクトをインスタンス化する構文です。

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOneメソッド

このメソッドは、現在のスレッドをブロックし、他のスレッドが信号を待機します。 WaitOneメソッドは、現在のスレッドをSleepスレッド状態にします。 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を返し、ループを抜けて「スレッドが信号を受け取った」というメッセージを表示します。

メソッドの設定

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オブジェクトのboolean変数をtrueに設定します。 待機中のスレッドはすべてブロック解除され、さらに進んでいきます。

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

manualResetEvent.Set();

リセットメソッド

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

以下に、Resetメソッドを呼び出す構文を示します。

manualResetEvent.Reset();

我々は、Setメソッドを呼び出した後、直ちにResetメソッドを呼び出す必要があります。


はい。 これは絶対に正しいです。

状態を示す方法としてManualResetEventを見ることができます。 何かがオン(設定)またはオフ(リセット)です。 持続時間のあるオカレンス。 その状態が発生するのを待っているスレッドはすべて処理を続行できます。

AutoResetEventは、シグナルに匹敵します。 ワンショットは何かが起こったことを示します。 持続時間のないオカレンス。 通常は起こった「何か」である必要はありませんが、小さいものであり、単一のスレッドで処理する必要があるため、単一のスレッドがイベントを消費した後の自動リセットが必要です。


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


通常、同じスレッドで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