使用带有 `Task.Run()` 的 `async` lambda 是多余的吗?
is using an an `async` lambda with `Task.Run()` redundant?
我刚刚遇到一些代码,例如:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
(不,我不知道 Foo.StartAsync()
的内部工作原理)。我最初的反应是摆脱 async
/await
并重写为:
var task = Foo.StartAsync();
task.Wait();
这是否正确(同样,对 Foo.StartAsync()
一无所知)。 answer to 似乎表明在某些情况下它可能有意义,但它也表示 "To tell the truth, I haven't seen that many scenarios ..."
大部分是。
像这样使用Task.Run
主要供不了解如何执行异步方法的人使用。
但是,还是有区别的。使用 Task.Run
意味着在 ThreadPool
线程上启动异步方法。
当异步方法的同步部分(第一个 await 之前的部分)很重要并且调用者想要确保该方法不阻塞时,这会很有用。
这也可以用于 "get out of" 当前上下文,例如没有 SynchronizationContext
.
通常,Task.Run
的预期 用法是在非 CPU 绑定代码上执行-UI 线程。因此,它很少与 async
委托一起使用,但它是可能的(例如,对于同时具有异步和 CPU 绑定部分的代码)。
但是,这是预期的用途。我认为在你的例子中:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
原作者更有可能试图同步阻塞异步代码,并且(滥用)使用 Task.Run
到 avoid deadlocks common in that situation(正如我在我的博客中描述的那样)。
本质上,它看起来像我在 article on brownfield asynchronous code 中描述的 "thread pool hack"。
最好的解决方案是不使用Task.Run
或Wait
:
await Foo.StartAsync();
这将导致 async
通过您的代码库增长,这是最好的方法,但现在可能会给您的开发人员带来无法接受的工作量。这大概就是您的前任使用 Task.Run(..).Wait()
.
的原因
值得注意的是,您的方法必须标记为 async
才能使用 await
关键字。
所编写的代码似乎是 运行 在同步上下文中使用异步代码的变通方法。虽然我不会说你永远不应该这样做,但在几乎所有情况下都最好使用异步方法。
// use this only when running Tasks in a synchronous method
// use async instead whenever possible
var task = Task.Run(async () => await Foo.StartAsync());
task.Wait();
异步方法,如您的 Foo.StartAsync()
示例,应始终 return 一个 Task
对象。这意味着使用 Task.Run()
创建另一个任务在异步方法中通常是多余的。通过使用 await
关键字可以简单地等待由异步方法执行的任务 return。您应该使用 Task.Run()
的唯一原因是当您执行需要在单独线程上执行的 CPU-bound 工作时。通过使用 await
关键字可以简单地等待由异步方法执行的任务 return。您可以在 Microsoft's guide to async programming.
中阅读更多详细信息
在异步方法中,您的代码可以像这样简单:
await Foo.StartAsync();
如果您想在任务 运行ning 期间执行一些其他工作,您可以将函数分配给一个变量,然后 await
结果(任务完成)。
例如:
var task = Foo.StartAsync();
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;
With CPU-bound work that needs to be 运行 on a separate thread, you can use Task.Run()
, but you're await
结果而不是使用线程阻塞Task.Wait()
:
var task = Task.Run(async () => await Foo.StartAsync());
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;
我刚刚遇到一些代码,例如:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
(不,我不知道 Foo.StartAsync()
的内部工作原理)。我最初的反应是摆脱 async
/await
并重写为:
var task = Foo.StartAsync();
task.Wait();
这是否正确(同样,对 Foo.StartAsync()
一无所知)。
大部分是。
像这样使用Task.Run
主要供不了解如何执行异步方法的人使用。
但是,还是有区别的。使用 Task.Run
意味着在 ThreadPool
线程上启动异步方法。
当异步方法的同步部分(第一个 await 之前的部分)很重要并且调用者想要确保该方法不阻塞时,这会很有用。
这也可以用于 "get out of" 当前上下文,例如没有 SynchronizationContext
.
通常,Task.Run
的预期 用法是在非 CPU 绑定代码上执行-UI 线程。因此,它很少与 async
委托一起使用,但它是可能的(例如,对于同时具有异步和 CPU 绑定部分的代码)。
但是,这是预期的用途。我认为在你的例子中:
var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
原作者更有可能试图同步阻塞异步代码,并且(滥用)使用 Task.Run
到 avoid deadlocks common in that situation(正如我在我的博客中描述的那样)。
本质上,它看起来像我在 article on brownfield asynchronous code 中描述的 "thread pool hack"。
最好的解决方案是不使用Task.Run
或Wait
:
await Foo.StartAsync();
这将导致 async
通过您的代码库增长,这是最好的方法,但现在可能会给您的开发人员带来无法接受的工作量。这大概就是您的前任使用 Task.Run(..).Wait()
.
值得注意的是,您的方法必须标记为 async
才能使用 await
关键字。
所编写的代码似乎是 运行 在同步上下文中使用异步代码的变通方法。虽然我不会说你永远不应该这样做,但在几乎所有情况下都最好使用异步方法。
// use this only when running Tasks in a synchronous method
// use async instead whenever possible
var task = Task.Run(async () => await Foo.StartAsync());
task.Wait();
异步方法,如您的 Foo.StartAsync()
示例,应始终 return 一个 Task
对象。这意味着使用 Task.Run()
创建另一个任务在异步方法中通常是多余的。通过使用 await
关键字可以简单地等待由异步方法执行的任务 return。您应该使用 Task.Run()
的唯一原因是当您执行需要在单独线程上执行的 CPU-bound 工作时。通过使用 await
关键字可以简单地等待由异步方法执行的任务 return。您可以在 Microsoft's guide to async programming.
在异步方法中,您的代码可以像这样简单:
await Foo.StartAsync();
如果您想在任务 运行ning 期间执行一些其他工作,您可以将函数分配给一个变量,然后 await
结果(任务完成)。
例如:
var task = Foo.StartAsync();
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;
With CPU-bound work that needs to be 运行 on a separate thread, you can use Task.Run()
, but you're await
结果而不是使用线程阻塞Task.Wait()
:
var task = Task.Run(async () => await Foo.StartAsync());
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;