我如何实现一个懒惰的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;
}

我如何实现一个懒惰的TaskCompletionSource

正如usr所说,不可能对Taskawait编辑做出反应。但是如果您可以使用自定义的可等待对象,那么您可以。

一个简单的方法是使用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更干净。

相关文章: