为什么"异步void"方法同步运行?

Han*_*ans 1 c# asynchronous async-await

Eric Lippert 在这里说:

无法等待返回异步方法的void; 这是一种"火与死"的方法.它确实异步工作 ......

它确实异步工作吗?

为了测试它,我制作了一个Windows窗体应用程序并处理了一个任意事件.在处理程序内部,我开始进行大量计算.显然,它会阻止UI响应:

this.KeyPress += Form1_KeyPressed;
....
private async void Form1_KeyPressed(object sender, EventArgs e)
{
   for(int i=0; i<int.max; i++)
      ;
}
Run Code Online (Sandbox Code Playgroud)

在Eric的回答中我错过了什么?

Eri*_*ert 13

在Eric的回答中我错过了什么?

我的意思是它像任何其他异步方法一样工作.当您在异步方法中等待某些东西时,该方法的其余部分将被注册为等待事物的延续.无论异步方法是否为空,都是如此.

在您的示例中,您的代码与返回任务的异步方法完全相同.尝试更改方法以返回任务,您将看到它的行为完全相同.

请记住,"异步"并不意味着"我在另一个线程上并发运行".这意味着"该方法可能在其动作完成之前返回".它在行动完成之前可以返回的点标有"等待".你没有用"等待"标记任何东西.

我怀疑你相信异步需要并发的神话.同样:异步只是意味着方法可以在其工作完成之前返回.你开始做一些鸡蛋,门铃响,你去门廊的包,你完成煮蛋,你打开包.在"煮鸡蛋"和"读取邮件"工作并不并发 -你从来没有他们在同一时间.它们是异步的.

  • @Hans:所有"异步"方法都是同步的*直到第一次等待.*这是该方法过早返回并将其余部分作为等待操作的继续进行签名的点. (3认同)

i3a*_*non 6

所有async关键字都允许您等待方法中的异步操作(并将结果包装在任务中).

每个异步方法都会同步运行,直到达到第一个await.如果你没有等待任何东西,那么这个方法(无论是返回任务还是不返回)将同步运行.

如果您的方法是同步的,通常根本不需要使用异步等待.但是如果你想将CPU密集型操作卸载到另一个线程,那么UI线程很长时间都不会被阻塞,你可以使用Task.Run:

await Task.Run(() => CPUIntensiveMethod());
Run Code Online (Sandbox Code Playgroud)


Sur*_*yan 5

只有分配法async不让它叫asynchronously.For异步调用它需要一种方法invokation与await它的关键字身体.

关于这个:

无法等待返回异步方法的void; 这是一种"火与死"的方法.它确实异步工作......

这意味着你不能在这样的另一个方法中等待THIS方法.

await Form1_KeyPressed(this, EventArgs.Empty)
Run Code Online (Sandbox Code Playgroud)

要使代码正常工作,您需要一个带有await关键字的方法:

private async void Form1_KeyPressed(object sender, EventArgs e)
{
   for(int i=0; i<int.max; i++)
      ;

   // In the body some code like this

   await YourMethod();

}
Run Code Online (Sandbox Code Playgroud)

更新后的版本

"异步"关键字

应用于方法时,"async"关键字的作用是什么?

当您使用"async"关键字标记方法时,您实际上是在告诉编译器两件事:

  1. 您告诉编译器您希望能够在方法中使用"await"关键字(当且仅当它所在的方法或lambda标记为async时,才可以使用await关键字).在这样做时,您告诉编译器使用状态机编译该方法,这样该方法将能够挂起,然后在等待点异步恢复.
  2. 您告诉编译器"提升"方法的结果或可能出现在返回类型中的任何异常.对于返回Task或Task的方法,这意味着在方法中未处理的任何返回值或异常都存储在结果任务中.对于返回void的方法,这意味着任何异常都会通过方法初始调用时当前的"SynchronizationContext"传播到调用者的上下文.

在方法上使用"async"关键字是否强制该方法的所有调用都是异步的?

不.当您调用标记为"async"的方法时,它会在当前线程上开始同步运行.因此,如果您有一个返回void的同步方法,并且您要做的就是将其标记为"async",那么该方法的调用仍将同步运行.无论您是将返回类型保留为"void"还是将其更改为"Task",都是如此.类似地,如果您有一个返回某些TResult的同步方法,并且您所做的只是将其标记为"async"并将返回类型更改为"Task",则该方法的调用仍将同步运行.

将方法标记为"异步"不会影响方法是同步还是异步运行.相反,它使该方法能够分成多个部分,其中一些部分可以异步运行,这样该方法可以异步完成.这些部分的边界只能在使用"await"关键字显式编码的情况下发生,因此如果在方法的代码中根本不使用"await",则只有一个部分,因为该部分将开始运行同步地,它(以及它的整个方法)将同步完成.

欲了解更多信息,请访问:http:

//blogs.msdn.com/b/pfxteam/archive/2012/04/12/async-await-faq.aspx

  • @Hans - 它**异步运行,*if*你使用`async`关键字提供给你的异步机制.这没有明确说明,但由于`async`和`await`总是串联引入,所以它是假设的. (2认同)