C# WaitHandle cancelable WaitAll

删除回忆录丶 提交于 2019-12-23 09:09:09

问题


I have the following code which has the goal to wait for all given wait handles but is cancellable by a specific wait handle:

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

This works only for ManualReset events. When using AutoReset events WaitAny resets all signalled events but returns only the first signalled (according MSDN).

Any ideas how to get this done with AutoReset events in a proper way without polling?


回答1:


I think that your method should work correctly as-written.

I believe that WaitHandle.WaitAny() uses the Windows API function WaitForMultipleObjects(), the documentation for which says:

Modification occurs only for the object or objects whose signaled state caused the function to return.

If true, it means that your code should work.

I wrote a test program. It creates a load of AutoResetEvents and sets half of them before calling CancelableWaitAll(). Then it starts a thread that waits 5 seconds before setting the other half of the AutoResetEvents. Immediately after starting that thread, the main thread calls CancelableWaitAll().

If the WaitAny() actually reset any of the autoreset events other than the one whose index was returned, the CancelableWaitAll() would never return.

Because it does return (after 5 seconds of course), I'm asserting that your code works with 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;
        }
    }
}

Unfortunately, I can't prove that WaitHandle.WaitAll() uses WaitForMultipleObjects(). However, if it didn't, you could call it yourself by using WaitHandle.SafeWaitHandle to get at the OS event handles and use P/Invoke to call WaitForMultipleObjects().



来源:https://stackoverflow.com/questions/15069467/c-sharp-waithandle-cancelable-waitall

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!