【问题标题】:I was wondering if using TaskCompletitionSource is a bad choice我想知道使用 TaskCompletitionSource 是否是一个糟糕的选择
【发布时间】:2020-01-31 09:21:54
【问题描述】:

我必须在这里补充一下,我不是 Stackoverflow 上的资深提问者,所以我很高兴收到有关我的问题可能不适合这里的反馈。

包装非异步调用时等待 TaskCompletitionSource 是一件坏事吗?

这是我的用例:

我有一个处理程序类,它在事件发生时调用函数Func<T, Task> 回调。处理程序从我的应用程序外部调用并通知我的 UI。 有两种方法 A 和 B 用作回调。 A 调用异步 HTTP 客户端,B 进行计算。在这两种情况下,await 调用都会解冻 UI,然后属性会得到更新。

答:

public async Task A(){
 result = await CallHttpClient(...) // unfreeze UI
 // ... copy image bytes and update UI (long running not async)
 // release bytes in calling method
}

乙:

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 冻结,这并不是我真正想要的,尽管它可能是最干净的解决方案。

-------> 更新

正如其他人所说,Task.Run() 在这里非常好。

我应该注意到我的 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() 找到更好的选择。

我还应该注意,当方法离开时,字节(示例中未显示)被释放。

【问题讨论】:

  • Task.Run 有什么问题?它不是为运行受 CPU 限制的方法而设计的吗?
  • 你应该改变标题和问题的前半部分来询问/解释你真正想要什么。 TaskCompletionSource 是一种工具,一种专门的工具。你想做什么,为什么不使用 awaitTask.Run
  • B 中的代码如何从await 发展到tcs.SetResult?它将被困在await
  • await tcs.Task; 不会解冻任何东西。 await 没有解冻,它等待一个已经异步的操作完成而不阻塞线程。如果该操作(或本例中的 tcs)从未完成,await 将不会返回
  • await httpClient.GetStringAsync() 中,GetStringAsync() 是一个异步操作,await 等待它而不阻塞线程,当它完成时在该线程上恢复执行。不是 await 使 GetStringAsync 成为异步方法

标签: c# .net task-parallel-library taskcompletionsource


【解决方案1】:

如果没有某种多线程,就无法在后台执行 CPU 密集型任务。

这段代码...

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() 的用途。

【讨论】:

  • 你说得对,我可以使用 Task.Run()。调用 Task.Run() 会很好。我应该写 tcs.SetResult(true);在等待之前。
猜你喜欢
  • 2015-12-27
  • 1970-01-01
  • 2011-01-11
  • 2014-09-07
  • 2014-01-06
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
相关资源
最近更新 更多