我刚刚遇到了一些代码:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
(不,我不知道Foo.StartAsync()
的内部运作方式)。我最初的反应是摆脱async
/ await
并重写为:
var task = Foo.StartAsync();
task.Wait();
这是否正确(再次,对Foo.StartAsync()
一无所知)。对This的What difference does it make - running an 'async' action delegate with a Task.Run ... 回答似乎表明可能存在可能有意义的情况,但它也说“说实话,我没见过那么多场景......”
答案 0 :(得分:12)
通常,Task.Run
的预期用法是在非UI线程上执行CPU绑定代码。因此,与async
委托一起使用它是非常罕见的,但它是可能的(例如,对于同时具有异步和CPU绑定部分的代码)。
然而,这是预期用途。我想在你的例子中:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
原作者尝试同步阻止异步代码的可能性更大,并且(ab)使用Task.Run
到avoid deadlocks common in that situation(正如我在博客中描述的那样)。
从本质上讲,它看起来像我在article on brownfield asynchronous code中描述的“线程池黑客”。
最佳解决方案是不使用Task.Run
或 Wait
:
await Foo.StartAsync();
这会导致async
在代码库中增长,这是最好的方法,但现在可能会给开发人员带来无法接受的工作量。这可能是您的前任使用Task.Run(..).Wait()
。
答案 1 :(得分:5)
大部分是的。
像这样使用Task.Run
主要用于那些不了解如何执行异步方法的人。
然而,存在差异。使用Task.Run
表示在ThreadPool
线程上启动异步方法。
当异步方法的同步部分(第一次等待之前的部分)很大并且调用者想要确保该方法不会阻塞时,这非常有用。
这也可用于"退出"当前上下文,例如,没有SynchronizationContext
。