我有以下代码,其目标是等待所有给定的等待句柄,但可以通过特定的等待句柄取消:
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事件的任何想法?
答案 0 :(得分:1)
我认为你的方法应该按照书面形式正确运作。
我相信WaitHandle.WaitAny()使用the Windows API function WaitForMultipleObjects(),其文档说:
仅对信号状态导致函数返回的对象或对象进行修改。
如果为true,则表示您的代码应该有效。
我写了一个测试程序。它会创建一个AutoResetEvents负载,并在调用CancelableWaitAll()之前设置其中的一半。然后它启动一个等待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来获取OS事件句柄并使用P / Invoke来调用WaitForMultipleObjects()。