我选择从我的对象方法返回Task<T>
和Task
,以便gui轻松使用。有些方法只是等待其他类型的等待句的互斥。有没有办法从Task
构造WaitHandle.Wait()
,这样我就不必为此阻止一个花圈池线程。
答案 0 :(得分:14)
有一种方法可以执行此操作:您可以使用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;
}
}
用法:
WaitHandle wh = new AutoResetEvent(true);
var task = wh.ToTask();
task.Wait();
答案 1 :(得分:4)
正如@gordy在谢尔盖·特普利亚科夫接受的回答评论中指出的那样,MSDN提出了取消订阅已登记的WaitHandle的实施。
我在这里略微修改它以支持回调的结果:如果注册超时,则任务返回false。如果已收到信号,则任务返回true:
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;
throw;
}
});
}
}
用法与原始答案相同:
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");
}