C# WaitHandle отменяется WaitAll
У меня есть следующий код, целью которого является ожидание всех заданных дескрипторов ожидания, но который можно отменить с помощью определенного дескриптора ожидания:
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. При использовании автоматического сброса событий WaitAny сбрасывает все сигнализированные события, но возвращает только первое сигнализированное (в соответствии с MSDN).
Любые идеи, как сделать это с событиями AutoReset надлежащим образом без опроса?
1 ответ
Я думаю, что ваш метод должен работать правильно, как написано.
Я считаю, что WaitHandle.WaitAny() использует функцию Windows API WaitForMultipleObjects (), документация для которой гласит:
Модификация происходит только для объекта или объектов, сигнальное состояние которых вызвало возврат функции.
Если это правда, это означает, что ваш код должен работать.
Я написал тестовую программу. Он создает загрузку 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, чтобы получить дескрипторы событий ОС, и использовать P/Invoke для вызова WaitForMultipleObjects().