C#WaitHandle可取消的WaitAll

本文关键字:WaitAll 可取消 C#WaitHandle | 更新日期: 2023-09-27 18:00:53

我有以下代码,它的目标是等待所有给定的等待句柄,但可以被特定的等待句柄取消:

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;
}

这仅适用于手动复位事件。当使用AutoReset事件时,WaitAny重置所有发出信号的事件,但只返回第一个发出信号的(根据MSDN(。

你知道如何在不进行轮询的情况下以正确的方式使用自动重置事件来完成这项工作吗?

C#WaitHandle可取消的WaitAll

我认为您的方法应该像编写的那样正常工作。

我相信WaitHandle。WaitAny((使用Windows API函数WaitForMultipleObjects((,其文档显示:

修改仅发生在其信号状态导致函数返回的一个或多个对象上。

如果为true,则表示您的代码应该可以工作。

我写了一个测试程序。它在调用CancelableWaitAll((之前创建一个AutoResetEvents负载并设置其中的一半。然后,它启动一个等待5秒的线程,然后再设置AutoResetEvents的另一半。启动该线程后,主线程立即调用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获取操作系统事件句柄,并使用P/Invoke调用WaitForMultipleObjects((。