如何实现惰性 TaskCompletionSource?
How can I implement a lazy TaskCompletionSource?
如果我的 TaskCompletionSource 公开的任务可能永远不会被调用,我该如何推迟结果的计算,除非并且直到有人等待该任务?
例如,我想阻止其他异步执行线程,直到使用以下函数 WaitOneAsync 发出 ManualResetEvent 信号。我在 ThreadPool.RegisterWaitForSingleObject 的回调中完成了 TaskCompleationSource,这在 WaitHandle 发出信号时发生。但是,如果没有人等待任务,那么我不想注册 WaitForSingleObject(如果在发出 WaitHandle 信号后等待任务,我也不想注册 WaitForSingleObject)。
我如何更改 WaitOneAsync,以便计算结果的工作,到 RegisterWaitForSingleObject,仅在有人等待 TaskCompleationSource.Task 之后发生?
我相信答案可能在于 Scott Chamberlain 在此处 中描述的自定义 TaskAwaiter,但我无法从他的示例中完全理解我的解决方案...:(
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;
}
这不可能完全按照您描述的要求进行。当有人添加延续或等待任务时,TPL 不提供事件或回调。
因此您需要构建 API 以便仅实际生成需要的任务。这个呢?
public static Func<Task<T>> CreateWaitOneAsyncFactory<T>(this WaitHandle waitHandle, Func<T> result) {
return () => WaitOneAsync(waitHandle, result);
}
这 return 是任务工厂而不是任务。这是作弊,但唯一可能的解决方案是这种作弊。
您也可以 return 自定义等待。但这根本不涉及任务,而且它忽略了任务的可组合性特征。 Awaitables 主要是 C# 概念。暴露它们会导致不干净 APIs.
与您的问题无关:您可以直接删除 await tcs.Task
和 return 该任务。此外,不需要 result
函数。 Return 一个 Task
没有结果。然后,呼叫者可以根据需要添加结果。这使得 WaitOneAsync
的 API 更干净。
正如 usr 所说,不可能对 Task
被 await
编辑做出反应。但是,如果您可以使用自定义 awaitable,那么您可以。
一个简单的方法是使用 AsyncLazy
from Stephen Cleary's 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));
如果我的 TaskCompletionSource 公开的任务可能永远不会被调用,我该如何推迟结果的计算,除非并且直到有人等待该任务?
例如,我想阻止其他异步执行线程,直到使用以下函数 WaitOneAsync 发出 ManualResetEvent 信号。我在 ThreadPool.RegisterWaitForSingleObject 的回调中完成了 TaskCompleationSource,这在 WaitHandle 发出信号时发生。但是,如果没有人等待任务,那么我不想注册 WaitForSingleObject(如果在发出 WaitHandle 信号后等待任务,我也不想注册 WaitForSingleObject)。
我如何更改 WaitOneAsync,以便计算结果的工作,到 RegisterWaitForSingleObject,仅在有人等待 TaskCompleationSource.Task 之后发生?
我相信答案可能在于 Scott Chamberlain 在此处
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;
}
这不可能完全按照您描述的要求进行。当有人添加延续或等待任务时,TPL 不提供事件或回调。
因此您需要构建 API 以便仅实际生成需要的任务。这个呢?
public static Func<Task<T>> CreateWaitOneAsyncFactory<T>(this WaitHandle waitHandle, Func<T> result) {
return () => WaitOneAsync(waitHandle, result);
}
这 return 是任务工厂而不是任务。这是作弊,但唯一可能的解决方案是这种作弊。
您也可以 return 自定义等待。但这根本不涉及任务,而且它忽略了任务的可组合性特征。 Awaitables 主要是 C# 概念。暴露它们会导致不干净 APIs.
与您的问题无关:您可以直接删除 await tcs.Task
和 return 该任务。此外,不需要 result
函数。 Return 一个 Task
没有结果。然后,呼叫者可以根据需要添加结果。这使得 WaitOneAsync
的 API 更干净。
正如 usr 所说,不可能对 Task
被 await
编辑做出反应。但是,如果您可以使用自定义 awaitable,那么您可以。
一个简单的方法是使用 AsyncLazy
from Stephen Cleary's 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));