【问题标题】:C# WaitHandle cancelable WaitAllC# WaitHandle 可取消的 WaitAll
【发布时间】:2013-02-25 23:25:50
【问题描述】:

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

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 事件完成此操作?

【问题讨论】:

  • 尝试使用其中一种重载方法。并尝试在进入do-while之前创建数组,也许你会有新的见解。
  • 如果引发取消事件,我无法理解这将如何工作?
  • 如果引发取消事件,则取消对所有给定 waitHandles 的等待
  • @Luke,这是因为取消句柄位于索引 0 处,而 while() 确保信号事件不在索引 0 处(handleIdx != 0)
  • @MatthewWatson 我现在明白了,干杯:)

标签: c# waithandle


【解决方案1】:

我认为您的方法应该可以正常工作。

我相信 WaitHandle.WaitAny() 使用 the Windows API function WaitForMultipleObjects(),其文档说:

仅对信号状态导致函数返回的一个或多个对象进行修改。

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

我写了一个测试程序。它创建一个 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 获取操作系统事件句柄并使用 P/Invoke 调用 WaitForMultipleObjects()。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多