I chose to return Task<T>
and Task
from my objects methods to provide easy consumation by the gui. Some of the methods simply wait for mutex of other kind of waithandles . Is there a way to construct Task
from WaitHandle.Wait()
so that I don't have to block one treadpool thread for that.
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
There is a way to do this: you can subscribe to WaitHandle using ThreadPool.RegisterWaitForSingleObject method and wrap it via TaskCompletionSource class:
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;
}
}
Usage:
WaitHandle wh = new AutoResetEvent(true);
var task = wh.ToTask();
task.Wait();
回答2:
As noted by @gordy in the comments of the accepted answer of Sergey Teplyakov, MSDN proposes an implementation with unsubscription of the registered WaitHandle.
I slightly modified it here to support the result of the callback: if the registration has timed out, the task return false. If the signal has been received, the task return 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;
}
});
}
}
Usage is same as the original answer:
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");
}