我必须在这里补充一下,我不是Stackoverflow的实践提问者,因此,我很高兴收到有关为何我的问题可能不适合此处的反馈。
包装一个非异步调用时,等待TaskCompletitionSource不好吗?
这是我的用例:
我有一个处理程序类,该类在事件发生时调用函数Func<T, Task>
回调。从我的应用程序外部调用该处理程序,并通知我的UI。有两种方法A和B用作回调。 A在这里调用异步HTTP客户端,B在这里进行计算。在这两种情况下,await调用都会解冻UI,然后更新属性。
A:
public async Task A(){
result = await CallHttpClient(...) // unfreeze UI
// ... copy image bytes and update UI (long running not async)
// release bytes in calling method
}
B:
public async Task B(){
var tcs = new TaskCompletionSource<bool>();
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
tcs.SetResult(true); // release bytes in calling method
}
我的问题在这里,使用TaskCompletionSource封装非异步调用是否是一种不好的做法?
该文档规定以下内容。
如果要为现有的异步操作或事件创建任务包装,请使用TaskCompletionSource。 https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
另一种可能性是调用Task.Run(),但对我来说感觉更糟。不使用Async会导致UI冻结,虽然这可能是最干净的解决方案,但这并不是我真正想要的UI。
------->更新
作为其他人的状态,Task.Run()在这里非常好。
我应该注意我的B:看起来与众不同B:
public async Task B(...){
var tcs = new TaskCompletionSource<bool>();
// ... duplicate bytes
tcs.SetResult(true); // release bytes in calling method
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
}
通过下面的Task.Run()查找更好的选项。
我还应注意,当方法离开字节时(示例中未显示)被释放。
var tcs = new TaskCompletionSource<bool>();
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
tcs.SetResult(true); // release bytes in calling method
...将阻塞await
,因为直到之后才调用SetResult
,从而导致某种死锁。
我想你可以像这样做些疯狂的事情
var tcs = new TaskCompletionSource<bool>(); Parallel.Invoke ( () => await tcs.Task, () => { // ... copy image bytes and update UI (long running not async) tcs.SetResult(true); // release bytes in calling method } );
但是我不确定这是否也可以。执行此操作的标准方法是
await Task.Run( () => {
// ... copy image bytes and update UI (long running not async)
});
...这当然更容易理解,并且它的含义是Task.Run()
。