如果我的TaskCompletionSource公开的Task可能永远不会被调用,那么除非有人在等待任务,否则我怎么能不计算结果的计算呢?
例如,我想阻止执行其他异步线程,直到使用以下函数WaitOneAsync发出ManualResetEvent信号。我在ThreadPool.RegisterWaitForSingleObject的回调中完成TaskCompleationSource,当WaitHandle发出信号时发生。但如果没有人等待任务,那么我不想要RegisterWaitForSingleObject(如果在WaitHandle发出信号后等待任务,我也不想要RegisterWaitForSingleObject)。
如何更改WaitOneAsync,以便计算结果到RegisterWaitForSingleObject的工作只发生在有人等待TaskCompleationSource.Task之后?
我相信答案可能在于Scott Chamberlain所描述的自定义TaskAwaiter Implement AsyncManualResetEvent using Lazy<T> to determine if the task has been awaited,但我无法从他的例子中得到我的解决方案...... :(
public static async Task<T> WaitOneAsync<T>(this WaitHandle waitHandle, Func<T> result) {
var tcs = new TaskCompletionSource<T>();
RegisteredWaitHandle rwh = null;
rwh = ThreadPool.RegisterWaitForSingleObject(
waitObject: waitHandle,
callBack: (s, t) => {
rwh.Unregister(null);
tcs.TrySetResult(result());
},
state: null,
millisecondsTimeOutInterval: -1,
executeOnlyOnce: true
);
return await tcs.Task;
}
答案 0 :(得分:4)
正如我们所说,对Task
await
private static Task<T> WaitOneAsyncImpl<T>(WaitHandle waitHandle, Func<T> result)
{
if (waitHandle.WaitOne(0))
return Task.FromResult(result());
var tcs = new TaskCompletionSource<T>();
RegisteredWaitHandle rwh = null;
rwh = ThreadPool.RegisterWaitForSingleObject(
waitObject: waitHandle,
callBack: (s, t) =>
{
rwh.Unregister(null);
tcs.TrySetResult(result());
},
state: null,
millisecondsTimeOutInterval: -1,
executeOnlyOnce: true
);
return tcs.Task;
}
public static AsyncLazy<T> WaitOneAsync<T>(this WaitHandle waitHandle, Func<T> result)
=> new AsyncLazy<T>(() => WaitOneAsyncImpl(waitHandle, result));
进行反应是不可能的。但是,如果你可以使用自定义等待,那么你可以。
一种简单的方法是使用AsyncLazy
from Stephen Cleary's AsyncEx:
public static boolean contains(final int[] array, final int key)
{
return Arrays.stream(array).anyMatch(n->n==key);
}
答案 1 :(得分:1)
这与您描述的要求完全不同。当有人添加延续或等待任务时,TPL不提供事件或回调。
因此,您需要构建API,以便仅实际生成所需的任务。那怎么样?
public static Func<Task<T>> CreateWaitOneAsyncFactory<T>(this WaitHandle waitHandle, Func<T> result) {
return () => WaitOneAsync(waitHandle, result);
}
这将返回任务工厂而不是任务。这是作弊,但唯一可能的解决方案涉及这种作弊。
您也可以返回等待的自定义。但这根本不涉及任务,它错过了任务的可组合性功能。 Awaitables主要是C#概念。暴露它们会导致不洁净的API。
与您的问题无关:您可以删除await tcs.Task
并直接返回该任务。此外,不需要result
功能。返回没有结果的Task
。然后呼叫者可以根据需要添加结果。这使得WaitOneAsync
的API更加清晰。