EventWaitHandleクラス

スレッドを同期できます。

EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
Task.Run(async () =>
{
    await Task.Delay(1000); // 時間のかかる処理
    waitHandle.Set();  // シグナル状態にする

    await Task.Delay(1000);
    waitHandle.Set();
});

waitHandle.WaitOne(); // シグナルを受け取るまで、スレッドはブロックされる
// EventResetModeがAutoResetのため、スレッドの解放後に自動で非シグナル状態となる

waitHandle.WaitOne(); // シグナルを受け取るまで、スレッドはブロックされる

コンストラクタ

public EventWaitHandle (
    bool initialState,                   // trueならば、初期状態でシグナル状態
    System.Threading.EventResetMode mode // イベントのリセットを、自動か手動のいずれで行うか
    );
EventWaitHandle(Boolean, EventResetMode) - EventWaitHandle コンストラクター (System.Threading) | Microsoft Learn

modeをAutoResetとするとAutoResetEvent、ManualResetとするとManualResetEventクラスに相当します。実際これらのクラスは、このコンストラクタをオーバーライドしているだけです。

[HostProtection(Synchronization=true, ExternalThreading=true)]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class AutoResetEvent : EventWaitHandle
{
    public AutoResetEvent(bool initialState) : base(initialState,EventResetMode.AutoReset){ }
}
autoresetevent.cs

AutoResetを指定すると、待機スレッドが解放された後にシグナル状態となったときに、自動的にリセットされます。ManualResetを指定すると、Reset()を呼ぶまでシグナル状態のままとなります。

EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.AutoReset);

bool w1 = waitHandle.WaitOne(0); // true … 自動で非シグナル状態となる
bool w2 = waitHandle.WaitOne(0); // false (非シグナル状態となっているため、シグナルを受け取れない)

waitHandle.Reset();
bool w3 = waitHandle.WaitOne(0); // false
bool w4 = waitHandle.WaitOne(0); // false
EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset);

bool w1 = waitHandle.WaitOne(0); // true … シグナル状態は変更されない
bool w2 = waitHandle.WaitOne(0); // true (シグナル状態のままのため、シグナルを受け取れる)

waitHandle.Reset();
bool w3 = waitHandle.WaitOne(0); // false
bool w4 = waitHandle.WaitOne(0); // false

プロパティ

プロパティ  
SafeWaitHandle SafeWaitHandle ネイティブのオペレーティングシステム ハンドル
IntPtr Handle ネイティブのオペレーティングシステム ハンドル

互換性のために残されています。代わりにSafeWaitHandleを用います。

現在のシグナル状態は、WaitOne(0)の戻り値で確認できます。

メソッド

メソッド 機能
WaitOne(Int32, Boolean) シグナル状態になるまで、現在のスレッドをブロックする
Set() シグナル状態にできる
Reset() 非シグナル状態にできる
Close() 保持されているすべてリソースを解放できる
   
メソッド - EventWaitHandle クラス (System.Threading) | Microsoft Learn

WaitOne()

このWaitHandleがシグナル状態になるか、タイムアウトするまで待機させられます。このメソッドは、Win32ではWaitForSingleObject()に相当します。

public virtual bool WaitOne (
    int millisecondsTimeout, // 待機するミリ秒数
    bool exitContext         // trueならば、同期されたコンテキスト内にいる場合は待機する前にコンテキストの同期ドメインを終了し、後で再取得する
    );
WaitOne(Int32, Boolean) - WaitHandle.WaitOne Method (System.Threading) | Microsoft Learn

millisecondsTimeoutに-1またはTimeout.Infiniteを指定すると、無限に待ちます。0を指定するとブロックせず、待機ハンドル (wait handle) の状態を確認し即座に返されます。省略すると-1となります。WaitOne - waithandle.cs

exitContextは、SynchronizationAttributeが指定されたContextBoundObjectクラスから派生したクラスで用いるような場合に影響します。省略するとfalseとなります。WaitOne exitContext

シグナルを受け取ったならばtrueが返されます。

EventWaitHandle waitHandle = new AutoResetEvent(false);

foo.SomeEvent += delegate
{
    // イベントが発生したならば、シグナル状態にする
    waitHandle.Set();
};

if (!waitHandle.WaitOne(1000))
{
    // 指定の時間内にシグナルを受け取らなかった
}

複数のWaitHandleを対象とするならば、WaitHandle.WaitAll()やWaitHandle.WaitAny()を用います。

WaitHandle.WaitAll()

WaitAll()は、スレッド モデルがSTAでは使用できません。WaitAll(WaitHandle[]) - WaitHandle.WaitAll Method (System.Threading) | Microsoft Learn

[STAThread]
static void Main()
{
    EventWaitHandle waitHandle1 = new EventWaitHandle(true, EventResetMode.AutoReset);
    EventWaitHandle waitHandle2 = new EventWaitHandle(true, EventResetMode.AutoReset);

    WaitHandle.WaitAll(new[] { waitHandle1, waitHandle2 }); // NotSupportedException「STA スレッドでの複数のハンドルの WaitAll はサポートされていません。」
}

この場合、アプリケーションをMTAとできるならばMTAThread属性を指定します。それが不可能ならばMTAとしたスレッドを作成し、そこでWaitAll()を実行します。

bool result;
Thread thread = new Thread(() =>
{
    result = WaitHandle.WaitAll(new[] { waitHandle1, waitHandle2 });
});

thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
thread.Join();
c# - How do I use WaitHandler.WaitAll in MSTest without STA warnings? - Stack Overflow

Set()

シグナル状態にできます。

AutoReset ManualReset
EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.AutoReset);

bool w1 = waitHandle.WaitOne(0); // true
bool w2 = waitHandle.WaitOne(0); // false

bool r1 = waitHandle.Reset(); // true
bool r2 = waitHandle.Reset(); // true (非シグナル状態で呼んでも問題ない)
bool w3 = waitHandle.WaitOne(0); // false
bool w4 = waitHandle.WaitOne(0); // false 

bool r3 = waitHandle.Set(); // true
bool r4 = waitHandle.Set(); // true (シグナル状態で呼んでも問題ない)
bool w5 = waitHandle.WaitOne(0); // true
bool w6 = waitHandle.WaitOne(0); // false
EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset);

bool w1 = waitHandle.WaitOne(0); // true
bool w2 = waitHandle.WaitOne(0); // true

bool r1 = waitHandle.Reset(); // true
bool r2 = waitHandle.Reset(); // true
bool w3 = waitHandle.WaitOne(0); // false
bool w4 = waitHandle.WaitOne(0); // false 

bool r3 = waitHandle.Set(); // true
bool r4 = waitHandle.Set(); // true
bool w5 = waitHandle.WaitOne(0); // true
bool w6 = waitHandle.WaitOne(0); // true

Reset()

イベントの状態を非シグナルに設定できます。これによりスレッドがブロックされるようにできます。

異なるスレッドから操作しても問題ありません。

EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset);

bool r1 = waitHandle.Reset();
bool r2 = waitHandle.Set();

Task.Run(() =>
{
    bool r3 = waitHandle.Reset();
    bool r4 = waitHandle.Set();
}).Wait();

Close()

このメソッドはDispose()からも呼ばれます。このメソッドの呼び出し後に他のメソッドを呼ぶと、「セーフ ハンドルは閉じられています。」としてObjectDisposedExceptionが投げられます。

ハンドルが閉じられていることは、SafeWaitHandle.IsClosedで確認できます。

EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
bool r1 = waitHandle.SafeWaitHandle.IsClosed; // false

waitHandle.Dispose();
bool r2 = waitHandle.SafeWaitHandle.IsClosed; // true
Microsoft Learnから検索