ManualResetEvent的包装纸作为awaitable任务 [英] Wrapping ManualResetEvent as awaitable task

查看:132
本文介绍了ManualResetEvent的包装纸作为awaitable任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想等待一个手动复位事件超时和观察注销。我想出了类似下面。手动复位事件对象超出了我的控制的API提供。有没有办法做到这一点,而不承担和阻塞线程线程池?

I'd like to await on a manual reset event with time-out and observing cancellation. I've come up with something like below. The manual reset event object is provided by an API beyond my control. Is there a way to make this happen without taking on and blocking a thread from ThreadPool?

static Task<bool> TaskFromWaitHandle(WaitHandle mre, int timeout, CancellationToken ct)
{
    return Task.Run(() =>
    {
        bool s = WaitHandle.WaitAny(new WaitHandle[] { mre, ct.WaitHandle }, timeout) == 0;
        ct.ThrowIfCancellationRequested();
        return s;
    }, ct);
}

// ...

if (await TaskFromWaitHandle(manualResetEvent, 1000, cts.Token))
{
    // true if event was set
}
else 
{
    // false if timed out, exception if cancelled 
}

显然,这有道理使用 RegisterWaitForSingleObject 。我给它一个尝试。

Apparently, it makes sense to use RegisterWaitForSingleObject. I'll give it a try.

推荐答案

RegisterWaitForSingleObject 将结合等待到专门的服务员线程,每个线程可以等待多个手柄(具体而言, 63人,这是 MAXIMUM_WAIT_OBJECTS 减一的控制手柄)。

RegisterWaitForSingleObject will combine waits onto dedicated waiter threads, each of which can wait on multiple handles (specifically, 63 of them, which is MAXIMUM_WAIT_OBJECTS minus one for a "control" handle).

所以,你应该能够使用这样的事情(警告:未经测试):

So you should be able to use something like this (warning: untested):

public static class WaitHandleExtensions
{
    public static Task AsTask(this WaitHandle handle)
    {
        return AsTask(handle, Timeout.InfiniteTimeSpan);
    }

    public static Task AsTask(this WaitHandle handle, TimeSpan timeout)
    {
        var tcs = new TaskCompletionSource<object>();
        var registration = ThreadPool.RegisterWaitForSingleObject(handle, (state, timedOut) =>
        {
            var localTcs = (TaskCompletionSource<object>)state;
            if (timedOut)
                localTcs.TrySetCanceled();
            else
                localTcs.TrySetResult(null);
        }, tcs, timeout, executeOnlyOnce: true);
        tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state).Unregister(null), registration, TaskScheduler.Default);
        return tcs.Task;
    }
}

这篇关于ManualResetEvent的包装纸作为awaitable任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆