使用`async` lambda与`Task.Run()`冗余?

Ðаn*_*Ðаn 17 .net c# asynchronous task

我刚刚遇到了一些代码:

var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
Run Code Online (Sandbox Code Playgroud)

(不,我不知道其内部运作方式Foo.StartAsync()).我最初的反应是摆脱async/ await并重写为:

var task = Foo.StartAsync();
task.Wait();
Run Code Online (Sandbox Code Playgroud)

这是否正确(再一次,一无所知Foo.StartAsync()). 这个答案与它有什么不同 - 用Task.Run运行'异步'动作委托......似乎表明可能存在可能有意义的情况,但它也说"说实话,我没有"看到很多场景......"

Ste*_*ary 13

通常,预期用途Task.Run是在非UI线程上执行CPU绑定代码.因此,它很少与async委托一起使用,但它是可能的(例如,对于具有异步和CPU绑定部分的代码).

但是,这是预期的用途.我想在你的例子中:

var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
Run Code Online (Sandbox Code Playgroud)

原始作者试图同步阻止异步代码的可能性更大,并且(ab)使用它Task.Run避免在那种情况下常见的死锁(正如我在我的博客中描述的那样).

本质上,它看起来像我在关于棕色异步代码的文章中描述的"线程池黑客" .

最好的解决办法是不使用Task.Run Wait:

await Foo.StartAsync();
Run Code Online (Sandbox Code Playgroud)

这将导致async您的代码库增长,这是最好的方法,但可能会给您的开发人员带来无法接受的工作量.这可能就是你的前任使用过的原因Task.Run(..).Wait().

  • "async"通过你的代码库增长的概念听起来类似于在某处引入`IDisposable`时所发生的事情(同时侧面讨论是否正确使用`IDisposable`的问题). (2认同)
  • @Dan:是的,`IDisposable`和`async`之间存在非常相似的设计问题. (2认同)

i3a*_*non 6

大多数是的.

Task.Run像这样使用主要是由不懂如何执行异步方法的人使用.

但是,存在差异.使用Task.Run意味着在ThreadPool线程上启动异步方法.

当异步方法的同步部分(第一次等待之前的部分)很大并且调用者想要确保该方法没有阻塞时,这可能很有用.

这也可以用于"摆脱"当前上下文,例如在没有a的情况下SynchronizationContext.