从WaitHandle.Wait构造任务

use*_*926 6 multithreading .net-4.0 task task-parallel-library c#-4.0

我选择返回Task<T>Task从我的对象方法中提供简单的gui消费.有些方法只是等待其他类型的等待句的互斥.有没有一种方法来构造Task,WaitHandle.Wait()以便我不必为此阻止一个treadpool线程.

Ser*_*kov 13

有一种方法可以执行此操作:您可以使用ThreadPool.RegisterWaitForSingleObject方法订阅WaitHandle 并通过TaskCompletionSource类包装它:

public static class WaitHandleEx
{
    public static Task ToTask(this WaitHandle waitHandle)
    {
        var tcs = new TaskCompletionSource<object>();

        // Registering callback to wait till WaitHandle changes its state

        ThreadPool.RegisterWaitForSingleObject(
            waitObject: waitHandle,
            callBack:(o, timeout) => { tcs.SetResult(null); }, 
            state: null, 
            timeout: TimeSpan.MaxValue, 
            executeOnlyOnce: true);

        return tcs.Task;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

WaitHandle wh = new AutoResetEvent(true);
var task = wh.ToTask();
task.Wait();
Run Code Online (Sandbox Code Playgroud)

  • 在MSDN上有一个稍微好一点的版本[从等待句柄到异步](https://msdn.microsoft.com/en-us/library/hh873178%28v=vs.110%29.aspx#WHToTap)它增加了一个ContinueWith取消注册已注册的等待 (6认同)

Eri*_*dil 9

正如@gordy 在 Sergey Teplyakov 接受的答案的评论中所指出的,MSDN提出了一个取消订阅已注册 WaitHandle 的实现。

我这里稍微修改了一下,支持回调的结果:如果注册超时,任务返回false。如果已收到信号,则任务返回真:

public static class ExtensionMethods
{
    public static Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeoutMs)
    {
        if (waitHandle == null)
            throw new ArgumentNullException(nameof(waitHandle));

        var tcs = new TaskCompletionSource<bool>();

        RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(
            waitHandle,
            callBack: (state, timedOut) => { tcs.TrySetResult(!timedOut); }, 
            state: null,
            millisecondsTimeOutInterval: timeoutMs, 
            executeOnlyOnce: true);
        
        return tcs.Task.ContinueWith((antecedent) =>
        {
            registeredWaitHandle.Unregister(waitObject: null);
            try
            {
                return antecedent.Result;
            }
            catch 
            {
                return false;
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

用法与原始答案相同:

WaitHandle signal = new AutoResetEvent(initialState: false);

bool signaled = await signal.WaitOneAsync(1000);
if (signaled)
{
    Console.WriteLine("Signal received");
}
else 
{
    Console.WriteLine("Waiting signal timed out");
}
Run Code Online (Sandbox Code Playgroud)