.NET 同期と非同期 EventWaitHandle

大家讲道理
大家讲道理オリジナル
2017-04-11 14:05:402795ブラウズ


前の記事で、Mutex とこの記事の主人公が直接的または間接的にから継承していることについて触れました。 WaitHandle:

  • Mutex クラス。これについては前の記事で説明しました。

  • EventWaitHandle クラスとその派生クラス AutoResetEvent および ManualResetEvent がこの記事の主役です。

    Se
  • map
  • horeクラス、つまりセマフォについては次の記事でお話します(いきなり紹介する必要はない気がします)

WaitHandleは同期のためのメソッドをいくつか提供しています。この記事では、インスタンス メソッドである WaitOne() についてすでに説明しています。さらに、WaitHandle には、

SignalAndWait(WaitHandle, WaitHandle): アトミック操作という 3 つの静的メソッドがあります。最初の WaitHandle にシグナルを送信し、2 番目の WaitHandle を待機します。つまり、最初の WaitHandle でブロックされたスレッド/プロセスを起動し、2 番目の WaitHandle を待機します。これら 2 つのアクションはアトミックです)。 オーバーロード

メソッド。 Int32 または
    Time
  • Span を使用して、待機タイムアウトと、コンテキストの同期ドメインから

    exitするかどうかを定義します。 WaitAll

    ( WaitHandle[]): これは待機するために使用されます。 WaitHandle 配列内のすべてのメンバーを対象とします。
  • ジョブが完了する前に全員を待機する必要がある場合は、タイムアウトを待機するオーバーロードされたメソッド
  • WaitAny(WaitHandle[]) を参照してください。 ): WaitAll() とは異なり、WaitAny は配列内のメンバーがシグナルを受信して​​いる限り戻ります。最も速く終了するものが開始され、WaitAny() には待機タイムアウトを制御するための 2 つのオーバーロードも必要になります。

  • スレッドの依存関係

ミューテックスは、Monitor と同様にスレッドに依存します。Monitor.Enter()/TryEnter() を通じて

object ロックを取得したスレッドのみが呼び出すことができると前に述べました。 Pulse()/Wait()/Exit() ;同様に、Mutex の所有権を取得したスレッドのみが ReleaseMutex() メソッドを実行でき、それ以外の場合は、逆に、EventWaitHandle と例外がスローされます。その派生クラス AutoResetEvent と ManualResetEvent はすべて無関係なスレッドです。どのスレッドも EventWaitHandle に信号を送り、ブロックされているスレッドを起動することができます。

  • 次の記事で説明するセマフォもスレッドに依存しません。

  • イベント通知

  • EventWaitHandle、AutoResetEvent、および ManualResetEvent にはすべて名前に「Event」が含まれていますが、これは .net 独自の

    event

    メカニズムとは何の関係もありません。
  • イベント。
プログラムを処理しています。これまでに遭遇した、スレッド間の「ロック」の競合を必要とする Monitor や Mutex と比較すると、スレッドの待機を必要とするいくつかの「イベント」として理解できます。スレッドは、これらのイベントが「発生」するのを待機することにより、自身をブロックします。 「イベント」が完了すると、ブロックされたスレッドはシグナルを受信した後も動作を継続できます。

WaitHandle 上の 3 つの静的メソッド SingnalAndWait()/WailAny()/WaitAll() と連携するために、EventWaitHandle は「Event」を完了して再開するための独自のメソッドを提供します:

bool:Set ():英語版 MSDN: イベントの状態をシグナル状態に設定し、1 つ以上の待機スレッドの続行を許可します。中国語版 MSDN: イベントの状態をシグナル状態に設定し、1 つ以上の待機スレッドの続行を許可します。イベント の状態を終了状態に変更し、1 つ以上の待機中のスレッドを続行できるようにします。一見、「通知」と「終了」は対応していないように見えますが、よく考えてみると、この 2 つの用語は実は矛盾していません。イベントが進行中の場合、もちろん「終了」はありません。イベントが完了すると、他のスレッドは待機する必要があります。そのため、待機中のスレッドを起動する信号を送信します。 「信号が送信されました」ステータスも妥当です。 2 つの細かい詳細:

  1. 中国語版でも英語版でも、このメソッドは「1 つ」または「複数」の待機スレッドを「継続/続行」できると記載されています (「ウェイクアップ」ではないことに注意してください)。したがって、このメソッドは、「ウェイクアップ」アクションの Monitor.Pulse() および Monitor.PulseAll() に似ています。どのような場合が Pulse() に似ているのか、またどのような場合が PulseAll() に似ているのかについては、読み続けてください。

  2. このメソッドの戻り値はブール値です。操作が成功した場合は true、それ以外の場合は false。ただし、MSDN では、実行がいつ失敗するかはわかりません。Microsoft MVP に問い合わせるしかありません。

  • bool:Reset(): イベントの状態を非シグナル状態に設定し、スレッドをブロックします。 イベントの状態を非シグナル状態に設定し、スレッドをブロックします。 同様に、「信号なし」と「終端なし」は同じものであることを理解する必要があります。また、意味のない戻り値がまだあります。 Reset() の機能は、イベントを再度「進行中」にすることと同じであり、その後、WaitOne()/WaitAll()/WaitAny()/SignalAndWait() イベントのすべてのスレッドが再びブロックされます。

  • Constructor

    EventWaitHandle の多くのコンストラクターの中で最も単純な 1 つを見てみましょう:

    • EventWaitHandle(BooleanInitialState, EventResetMode mode): の新しいインスタンスを初期化します。 EventWaitHandle クラス、および待機ハンドルが最初に終了状態にあったかどうか、および待機ハンドルが自動的にリセットされたか手動でリセットされたかを指定します。ほとんどの場合、新しいインスタンスがデフォルトで「非終了」状態になるように、最初のパラメータに false を使用します。 2 番目のパラメーター EventResetMode は、合計 2 つの値を持つ列挙です:

    1. EventResetMode.AutoReset: Set() が呼び出され、現在の EventWaitHandle が終了状態になるとき、スレッドが現在の EventWaitHandle でブロックされている場合、 スレッドを解放した後、EventWaitHandle は自動的にリセット (Reset() を自動的に呼び出すのと同じ) し、再び非終了状態に入り、最初にブロックされていた残りのスレッド (存在する場合) は引き続きブロックされます。 Set() の呼び出し後にブロックされたスレッドがない場合、EventWaitHandle は、スレッドがイベントを待機しようとするまで「終了」状態のままになります。その後、このスレッドは自動的にリセットされ、ブロックされません。それ以降のすべてのスレッド。

    2. EventResetMode.ManualReset: 終了すると、EventWaitHandle は待機中のスレッドすべてを解放し、手動でリセットされるまで、つまり Reset() が呼び出される前に終了したままになります。

    さて、Set() がそれぞれ Monitor.Pulse()/PulseAll() に似ていることを明確に知ることができます:

      EventWaitHandle が AutoReset モードで動作すると、関数が起動されます。 , Set() は Monitor.Pulse() に似ています。現時点では、Set() はブロックされた多数のスレッド (複数ある場合) のうちの 1 つだけをウェイクアップできます。しかし、この 2 つにはまだいくつかの違いがあります。
      Set() は単に「ウェイクアップ」するだけでなく「解放」し、スレッドが引き続き動作 (続行) できるようにします。 ) は実行ステータスに戻り、オブジェクト ロックの競争に参加するだけで、オブジェクト ロックを取得できるかどうかは誰も保証できません。
    1. Pulse() の呼び出された状態は維持されません。したがって、待機中のスレッドがないときに Pulse() が呼び出された場合、Monitor.Wait() を呼び出す次のスレッドは、Pulse() が呼び出されなかったかのようにブロックされます。つまり、次の WaitXXX() まで継続する Set() とは異なり、Monitor.Pulse() は呼び出されたときにのみ有効になります。
    ManualReset モードで動作する EventWaitHandle の Set() メソッドが呼び出されると、そのウェイクアップ関数は Monitor.PulseAll() に似ています。ブロックされているすべてのスレッドがシグナルを受信して​​ウェイクアップされます。両者の違いは上記と全く同じです。
  • EventWaitHandle の他のコンストラクターを見てみましょう:
    • EventWaitHandle(BooleanInitialState, EventResetMode mode, String name): 最初の 2 つのパラメーターについてはすでに説明しました。3 番目のパラメーター名は、システム全体の同期イベントの名前を指定するために使用されます。はい、Mutex の記事で述べたように、親クラス WaitHandle には Mutex と同様にプロセス ドメインを横断する機能があるため、グローバル EventWaitHandle を作成し、後でそれをプロセス間通知に使用できます。名前では依然として大文字と小文字が区別され、命名プレフィックスの問題がまだ存在することに注意してください。こちらを参照してください。これは、名前が null または空の string の場合に、ローカルの名前のない EventWaitHandle を作成するのと同じです。それでも同じですが、システム内に同じ名前の EventWaitHandle がすでに存在するため、同じ名前の EventWaitHandle を表すインスタンスが 1 つだけ返される可能性があります。したがって、最終的には同じです。この EventWaitHandle が最初に自分によって作成されたかどうかを知る必要がある場合は、次の 2 つのコンストラクターのいずれかを使用する必要があります。

    • EventWaitHandle(BooleanInitialState, EventResetMode mode, String name, out Boolean createdNew): createdNew は EventWaitHandle が正常に作成されたかどうかを示すために使用され、true は成功を示し、false は同じ名前のイベントが既に存在することを示します。

    • EventWaitHandle(BooleanInitialState, EventResetMode mode, String name, out Boolean createdNew, EventWaitHandleSecurity): セキュリティの問題については、このコンストラクターの例を確認してください。グローバル MutexEventWaitHandle のセキュリティ問題は、Mutex の問題よりも注意を払う必要があります。ハッカーが同じイベント名を使用してシグナルを送信したり、スレッドを整理したりする可能性があり、ビジネス ロジックに重大な損害を与える可能性があるからです。

    MSDN デモ


    using System;using System.Threading;public class Example
    {    // The EventWaitHandle used to demonstrate the difference    // between AutoReset and ManualReset synchronization events.    //    private static EventWaitHandle ewh;    // A counter to make sure all threads are started and    // blocked before any are released. A Long is used to show    // the use of the 64-bit Interlocked methods.    //    private static long threadCount = 0;    // An AutoReset event that allows the main thread to block    // until an exiting thread has decremented the count.    //    private static EventWaitHandle clearCount = 
            new EventWaitHandle(false, EventResetMode.AutoReset);
    
        [MTAThread]    public static void Main()
        {        // Create an AutoReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.AutoReset);        // Create and start five numbered threads. Use the        // ParameterizedThreadStart delegate, so the thread        // number can be passed as an argument to the Start 
            // method.
            for (int i = 0; i <= 4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        // When multiple threads use a 64-bit value on a 32-bit        // system, you must access the value through the        // Interlocked class to guarantee thread safety.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Release one thread each time the user presses ENTER,        // until all threads have been released.        //        while (Interlocked.Read(ref threadCount) > 0)
            {
                Console.WriteLine("Press ENTER to release a waiting thread.");
                Console.ReadLine();            // SignalAndWait signals the EventWaitHandle, which            // releases exactly one thread before resetting, 
                // because it was created with AutoReset mode. 
                // SignalAndWait then blocks on clearCount, to 
                // allow the signaled thread to decrement the count            // before looping again.            //            WaitHandle.SignalAndWait(ewh, clearCount);
            }
            Console.WriteLine();        // Create a ManualReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.ManualReset);        // Create and start five more numbered threads.        //        for(int i=0; i<=4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Because the EventWaitHandle was created with        // ManualReset mode, signaling it releases all the        // waiting threads.        //        Console.WriteLine("Press ENTER to release the waiting threads.");
            Console.ReadLine();
            ewh.Set();
    
        }    public static void ThreadProc(object data)
        {        int index = (int) data;
    
            Console.WriteLine("Thread {0} blocks.", data);        // Increment the count of blocked threads.
            Interlocked.Increment(ref threadCount);        // Wait on the EventWaitHandle.        ewh.WaitOne();
    
            Console.WriteLine("Thread {0} exits.", data);        // Decrement the count of blocked threads.
            Interlocked.Decrement(ref threadCount);        // After signaling ewh, the main thread blocks on        // clearCount until the signaled thread has 
            // decremented the count. Signal it now.        //        clearCount.Set();
        }
    }


    以上が.NET 同期と非同期 EventWaitHandleの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。