我如何实现一个懒惰的TaskCompletionSource
本文关键字:一个 TaskCompletionSource 实现 何实现 | 更新日期: 2023-09-27 18:15:33
如果我的TaskCompletionSource暴露的任务可能永远不会被调用,我怎么能延迟计算的结果,除非和直到有人等待任务?
例如,我想要阻塞其他异步线程的执行,直到使用以下函数WaitOneAsync发出ManualResetEvent的信号。我在ThreadPool的回调中完成了TaskCompleationSource。RegisterWaitForSingleObject,当WaitHandle被发出信号时发生。但是如果没有人等待任务,那么我不想要RegisterWaitForSingleObject(也不想要RegisterWaitForSingleObject,如果任务在WaitHandle发出信号后等待)。
我怎么能改变WaitOneAsync,使工作计算的结果,以注册waitforsingleobject,只发生在有人等待TaskCompleationSource.Task后?
我相信答案可能就在这里描述的自定义TaskAwaiter中使用lazy实现AsyncManualResetEvent来确定斯科特·张伯伦是否已经完成了这项任务,但我不能从他的例子中得到我的解决方案……(
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;
}
正如usr所说,不可能对Task
被await
编辑做出反应。但是如果您可以使用自定义的可等待对象,那么您可以。
AsyncLazy
从Stephen Cleary的AsyncEx:
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));
这是不可能完全按照您描述的需求。当有人添加延续或等待任务时,TPL不提供事件或回调。
因此,您需要构建API,以便仅实际生成所需的任务。这个呢?
public static Func<Task<T>> CreateWaitOneAsyncFactory<T>(this WaitHandle waitHandle, Func<T> result) {
return () => WaitOneAsync(waitHandle, result);
}
返回任务工厂而不是任务。这是作弊,但唯一可能的解决办法就是这种作弊。
你也可以返回一个自定义的可等待对象。但这根本不涉及任务,而且它忽略了任务的可组合性特性。可等待对象主要是c#概念。暴露它们会导致不干净的api。
与您的问题无关:您可以删除await tcs.Task
并直接返回该任务。此外,也不需要result
函数。返回一个没有结果的Task
。然后,调用者可以根据需要添加一个结果。这使得WaitOneAsync
的API更干净。