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

时间:2015-11-16 08:50:59

标签: c# asynchronous async-await

Here Eric Lippert说:

  

无法等待返回异步方法的void;这是一场“火与...   忘记“方法。 它确实异步工作 ......

它确实可以异步工作吗?

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

t= Test.unscoped.new; t.successful
=> true

我在Eric的回答中缺少什么?

7 个答案:

答案 0 :(得分:13)

  

Eric的答案中我错过了什么?

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

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

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

我怀疑你相信异步需要并发的神话。再次:异步只是意味着方法可以在其工作完成之前返回。你开始煮一些鸡蛋,门铃响,你去门廊的包装,你完成煮鸡蛋,你打开包。 "煮鸡蛋"并且"获取邮件"作业不是并发 - 您从未同时同时。它们是异步

答案 1 :(得分:6)

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

每个异步方法都会同步运行,直到达到第一个await。如果您没有等待任何事情,那么此方法(无论是返回任务还是不返回任务)将同步运行。

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

await Task.Run(() => CPUIntensiveMethod());

答案 2 :(得分:5)

只有async的分配方法才能使其异步调用。对于异步调用,需要使用await关键字进行方法调用 body < /强>

关于这个:

  

无法等待返回异步方法的void;这是一场火灾和火灾   忘记&#34;方法。它确实异步工作......

这意味着您无法在其他方法中等待 THIS 方法。

await Form1_KeyPressed(this, EventArgs.Empty)

要使代码正常工作,您需要一个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();

}

更新版本

“异步”关键字

“async”关键字在应用于方法时的作用是什么?

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

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

    没有。当您调用标记为“async”的方法时,它会在当前线程上开始同步运行。 因此,如果您有一个返回void的同步方法,并且您要做的就是将其标记为“async”,那么该方法的调用仍将同步运行。无论您是否离开,都是如此返回类型为“void”或将其更改为“Task”。类似地,如果你有一个返回一些TResult的同步方法,你所做的就是将它标记为“async”并将返回类型更改为“Task”,那么该方法的调用仍然会同步运行。

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

    有关详情,请参阅此处:

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

答案 3 :(得分:3)

Async / await允许您创建可以异步运行的代码。它甚至可以并行运行(与异步正交)。但是,它是否并行运行取决于您的任务的安排方式。

当您从UI或ASP.NET上下文调用代码时(更具体地说,来自这些框架中具有UI控件的上下文中的主线程,因为大多数控件只能在拥有它们的线程上访问),默认情况下不会安排到后台线程。您仍将看到代码的执行将在继续之前等待Task完成,但由于Task被调度到同一(主)线程,因此它将阻止该线程上的任何其他操作(例如,处理UI事件)。

如果你知道你的代码在后台线程中是安全的(再次,这通常是因为你知道你访问任何线程仿射的控件属性),你可以覆盖使用ConfigureAwait安排行为:

<div id="word_content">
<br>Testing Time: 2015-10-29 17:57:11<br>
    Total Age: 19<br>
    Total Friemd: 9<br>
    Total Family: 10<br>
    <br>
Here are the suggestions  - Him_530037_: <a href="www.mytarget.com="_blank">93358546</a>
<h3>Overview</h3><br>
<ul>
    <li>(The overlap provided is not good)</li>
</ul>

<h3>Structure</h3><br>
<h4>Target:</h4><br>
<ul>
    <li>Audience.</li>
    <li>Lookalike</li>
    <li>Overlap of Audience</li> 
    <a href="https://www.myPage.com/lolPagess/?id=06" target="_blank">06<font name="names" hidden="" style="display: inline;"> - Page Likes</font></a>           
</ul>

另请参阅:Async/Await - Best Practices in Asynchronous Programming

答案 4 :(得分:1)

Async只是一个声明,您可以使用异步方法。如果你要创建一个像

这样的任务
private async Task<int> callMe()
{
   int i;
   for(i = 0; i<int.max; i++)
      ;
   return i;
} 

您可以在KeyPressed事件中使用 await callMe(); 行运行代码。当然,您不必返回任何值,但这只是一个实际的例子。

答案 5 :(得分:0)

仅仅因为你声明一个方法async并不能让它自动在另一个线程中运行 你必须自己用Task.Run()开始一个。

所以在你的情况下,这将是:

private async void Fomr1_KeyPressed(object sender, EventArgs e)
{
   await Task.Run(() => {
       for(int i=0; i<int.max; i++);
   });
}

async/await的一些好起点:
Asynchronous Programming with Async and Await
Parallel Programming with .NET
Task Schedulers

答案 6 :(得分:0)

我从CLS Compilance发现这个解释非常有启发性:

  

现在记住await后的所有内容非常重要   关键字在延续中执行。使用之间的区别   awaitContinueWith是继续实际上是在   调用者线程,在这种情况下是UI线程。

所以:

private async void Form1_KeyPressed(object sender, EventArgs e)
{
   // ----------------------> Surely, on UI thread
   await Something(); ------> (May be) on new thread
   // ----------------------> Surely, on UI thread
   for(int i=0; i<int.max; i++)
      ;
}