6

次のコードがあります。これは、指定されたすべての待機ハンドルを待機することを目的としていますが、特定の待機ハンドルによってキャンセルできます。

public static bool CancelableWaitAll(WaitHandle[] waitHandles, WaitHandle cancelWaitHandle)
{
    var waitHandleList = new List<WaitHandle>();
    waitHandleList.Add(cancelWaitHandle);
    waitHandleList.AddRange(waitHandles);
    int handleIdx;
    do
    {
        handleIdx = WaitHandle.WaitAny(waitHandleList.ToArray());
        waitHandleList.RemoveAt(handleIdx);
    }
    while (waitHandleList.Count > 1 && handleIdx != 0);
    return handleIdx != 0;
}

これは、ManualResetイベントに対してのみ機能します。AutoResetイベントを使用する場合、WaitAnyはすべてのシグナルイベントをリセットしますが、最初にシグナルされたイベントのみを返します(MSDNによる)。

ポーリングせずに適切な方法でAutoResetイベントを使用してこれを実行する方法はありますか?

4

1 に答える 1

1

私はあなたの方法が書かれた通りに正しく働くべきだと思います。

WaitHandle.WaitAny()はWindows API関数WaitForMultipleObjects( )を使用していると思います。

変更は、シグナル状態によって関数が戻る原因となった1つまたは複数のオブジェクトに対してのみ発生します。

trueの場合、コードが機能するはずです。

テストプログラムを書きました。AutoResetEventsのロードを作成し、CancelableWaitAll()を呼び出す前にそれらの半分を設定します。次に、AutoResetEventsの残りの半分を設定する前に5秒間待機するスレッドを開始します。そのスレッドを開始した直後に、メインスレッドはCancelableWaitAll()を呼び出します。

WaitAny()が、インデックスが返されたイベント以外の自動リセットイベントのいずれかを実際にリセットした場合、CancelableWaitAll()は決して戻りません。

(もちろん5秒後に)戻るので、あなたのコードはAutoResetEventsで動作すると断言しています:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            AutoResetEvent[] events = new AutoResetEvent[32];

            for (int i = 0; i < events.Length; ++i)
            {
                events[i] = new AutoResetEvent(false);
            }

            // Set the first 16 auto reset events before calling CancelableWaitAll().

            for (int i = 0; i < 16; ++i)
            {
                events[i].Set();
            }

            // Start a thread that waits five seconds and then sets the rest of the events.

            Task.Factory.StartNew(() => setEvents(events));

            Console.WriteLine("Waiting for all events to be set.");

            ManualResetEvent stopper = new ManualResetEvent(false);
            CancelableWaitAll(events, stopper);

            Console.WriteLine("Waited.");
        }

        private static void setEvents(AutoResetEvent[] events)
        {
            Thread.Sleep(5000);

            for (int i = 16; i < events.Length; ++i)
            {
                events[i].Set();
            }
        }

        public static bool CancelableWaitAll(WaitHandle[] waitHandles, WaitHandle cancelWaitHandle)
        {
            var waitHandleList = new List<WaitHandle>();
            waitHandleList.Add(cancelWaitHandle);
            waitHandleList.AddRange(waitHandles);
            int handleIdx;
            do
            {
                handleIdx = WaitHandle.WaitAny(waitHandleList.ToArray());
                waitHandleList.RemoveAt(handleIdx);
            }
            while (waitHandleList.Count > 1 && handleIdx != 0);
            return handleIdx != 0;
        }
    }
}

残念ながら、WaitHandle.WaitAll()がWaitForMultipleObjects()を使用していることを証明できません。ただし、そうでない場合は、WaitHandle.SafeWaitHandleを使用してOSイベントハンドルを取得し、P / Invokeを使用してWaitForMultipleObjects()を呼び出すことにより、自分で呼び出すことができます。

于 2013-02-25T15:17:27.173 に答える