欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于async和await的一些误区实例详解

程序员文章站 2024-02-17 08:36:16
微软官方的msdn上说async和await是“异步”,但是不少人(包括笔者自己)都有一些误区需要澄清:为什么await语句之后没有执行?不是异步吗? 先举一个示例代码如...

微软官方的msdn上说async和await是“异步”,但是不少人(包括笔者自己)都有一些误区需要澄清:为什么await语句之后没有执行?不是异步吗?

先举一个示例代码如下:

public partial class form1 : form
{
 public async task processing()
 {
   await task.delay(5000);
   label1.text = "succuessful";
 }
 public form1()
 {
   initializecomponent();
   
 }
 private async void button1_click(object sender, eventargs e)
 {
   await processing();
   messagebox.show("button's event completed");
 }
}

很多人(包括笔者)一开始会觉得异步好像类似多线程一样,到await的时候会在后台先开启一个线程执行任务,随后主线程(这里是ui线程)将自动执行后面的部分(即弹出“button's event completed”的消息框)。

其实这个理解是错误的。async和await的本质其实是“yield return”和“linq”的“迭代式”等待。我们应该清楚一点:那就是你写了linq语句:

var results = from ……
       select ……;

foreach(var r in results)
{
 ……
}

当你下断点你会发觉results并不会立即执行,直到使用到results的地方(例子中也就是foreach这里)才会被执行(此时黄色跟踪调试的光棒又会折回到var results……这里,然后等到results执行完毕之后才真正进入foreach进行执行)。

所以,async/await和linq的这种“迭代式”的“异步操作”是异曲同工的。只不过async/await本质是返回一个task而已,而task又是异步的(因为task本质就是一个线程),所以真正执行到(使用到async方法的时候)带有await的方法的时候,后台才会真正开启一个线程去执行任务。此时主线程会等待这个task线程直到其执行完毕(iscomplete属性为true为止)。所以界面是不会卡顿的。

所以,await是task的异步等待而已,并不是我们所谓的“异步操作”;拿它和linq作对比,你会发现linq执行顺序和它一致,只不过linq没有异步等待(当然没有!又没有开启线程啥的……)。

我们进一步可以这样对比:

linq:变量 = linq语句(表达式)

   等到使用linq变量的时候才折返到linq语句处真正执行linq语句。

异步等待:变量 = 异步方法

    等到使用await+异步方法的时候才会折返到该异步方法处,开启线程真正执行异步方法,主线程被挂起(但不会造成界面死掉),直至子线程task任务完全执行完毕为止。

在linq中,你如果需要立即执行,可以使用扩展方法:

var results = (from ……
              select ……).tolist();
因为立即使用到了这个linq语句,所以会被立即执行。

同样地,异步等待也可以变成类似wait一样的同步等待:

private async void button1_click(object sender, eventargs e)
{
  processing().getawaiter().getresult();
  messagebox.show("button's event completed");
}

因为processing本来就返回task,当然也可以使用wait进行同步等待。