HTML5技术

await和async更多的理解 - 谷歌’s

字号+ 作者:H5之家 来源:H5之家 2017-10-28 16:01 我要评论( )

最近有不少网友提起await和async,呵呵,C# 5引进的语法糖。 这个语法糖还真不好吃,能绕倒一堆初学的朋友,在网上也有很多网友关于这块知识点的争论,有对有错,今天在这里把这个误区好好讲讲。 在await(C# 参考)这样写道: “await运算符应用于异步方法

最近有不少网友提起await和async,呵呵,C# 5引进的语法糖。

这个语法糖还真不好吃,能绕倒一堆初学的朋友,在网上也有很多网友关于这块知识点的争论,有对有错,今天在这里把这个误区好好讲讲。

在await(C# 参考)这样写道:

“await 运算符应用于异步方法中的任务,在方法的执行中插入挂起点,直到所等待的任务完成。 任务表示正在进行的工作。”


不要小看这两句话,内容里暗指的意思还真不少。

1)await 运算符针对于异步方法

2)await 插入挂起点

3)await 等待任务完成

4)任务表式正在进行的工作

带着上面四点,我们暂时停下,因为提到await不禁要联想到一个好基友aysnc

在await(C# 参考)这样写道:

"await 仅可用于由 async 关键字修改的异步方法中"


到这里,我们对上面的四个关键点,提出疑问。

await 运算符针对于异步方法,那如果我们在异步方法里添加入同步方法会怎么样?

Task<TResult> XAsync() { X(); XXAsync(); //XXAsync()异步方法 }

 

然后在mainTest主线程里调用这个XAsync()方法

static void Main(string[] args) { XAsync();

OtherMethod(); }

 

 

在main方法里,网上有网友博客说道:

 

1)XAsync在主线程里不会被调用,直到 awiat XAsync 才会被成功调用。就像linq to sql表达式一像,首先是var results=array.select().where();语句一样,他们只是组装,
并不会执行,要等到foreach(var result in results){ ...}迭代时或者.toList()再真正的查询。

2)XAsync在主线程里会被调用,并不阻止主线程,主线程将继续执行下面的程序!原因是我们写这个方法时候,vs会给我们警告,有图为据

到底是谁正确呢?

呵呵,其实这里两种说法都不正确。

首先,XAsync()在主线程里,直接调用,肯定执行,VS此时也瞎胡闹,警告我们说,“不等待此方法”,这是有个大大的前提!那就是这个方法体内,必须是异步的! 可能说到此不好理解。

在XAsync()方法里,上面有一段同步方法X(),此时它是运行在主线程上的同步方法,会阻止主线程,在X()运行完全后,在运行至 return await XXAsync()时,才把主线程调用权交还给调用的方法

在此过程里,并不会产生新的线程,全部运行在主线程上

呵呵,越说越迷糊了。。。

await和async 讲白了点,他们并不会真正意义上的去产生新的线程,我们都知道,产生线程可以用Task类或Thread类。
那么async 标注的方法到底是什么呢?微软给我们的一句单简的话,"await 仅可用于由 async 关键字修改的异步方法中"

这就说明,async是为了await起到一种“配合”作用。而是说async修饰的方法都是异步的,那也太相当然了。

在同步方法里,执行具有async修饰的方法,按同步方法来执行。也就是说X()是运行在主线程上的方法。

至于X()方法体内至于执行同步还是异步,决定权由方法体内的方法是否据有异步功能!
就像我们上面一样,XAsync()方法体内的X()就同步,那么X()执行的依然是同步方法一样,而不是运行另外一线程上。

那么问题来了,那我们都直接X()方法多好,await还有何用,或者await 为何不直接调用下X()方法呢?。。。

于是我们继续看下一句XXAsync()方法,我们既然用了await 语法,为什么他可以用?我可以把他去了await吗?

当然是肯定的,就像这样:

Task<TResult> XAsync() { X(); //X()同步方法0 XXAsync();XXXAsync(); //XXAsync()异步方法2 }



XXAsync() 此时是如何运行的呢?同步还是异步?问题又返回来了,至于同步还是异步,不是这个方法“名词”决定的,而是这个方法体内是如何执行的

如:


Task XXAsync() { X(); }

 

此时像上面调用的方式,XXAsync()就是我们平时的同步方法嘛!

但是改下:

Task XXAsync() { Task.Run(() =>{ X(); }); }



依据用相同的方法调用XXAsync它,这个时候,真正的运行在另外“任务上”了哦,会运行在其它线程!

写到这里,我们并没有和await有关哦。

那么在XXAsync()方法前加await到底有何不同?

这里首先要澄清的是:加不加 await 与 XXAsync()是异步还是同步的并没有关系!

await 真正的目的只有一个 在调用异步方法 XXAsync() 时挂起此方法,它认为这个方法是比较耗时的方法,主线程或调用此方法的线程不要在此方法等待。 并同时作个标记,当前执行的线程运行结束时,将由此处恢复运行,所以在await 标记的异步方法下面的代码或方法将不能运行,必须等待这个方法完成!


如:

Task XAsync() { await XXAsync(); OtherMothod(); }

 

在运行至 await XXAsync()时,调用方法XAsync()者将不再运行,直接返回,就像我们程序代码中的return语句。这样做的好处是,调用线程,不将等待此耗时线程。直接让调用线程往下运行,

如果调用线程向上一直存在await 链,就像方法套方法里有return一样,会逐层向上返回,一直返回到主线程。

而每个“子线程”去等待耗时的I/O处理,比如 操作数据库和网络流任何,这里特别强调I/O处理,试想下,我们的程序要查询一个数据库,可能要有点时间,此时查询命令可能已运行在了sql数据库里,

如果数据库在远程另外一台机器上呢?我们的"子线程或者任务“只是等待,而此时的主线程可能已完成。

如何理解主线程已完成呢?Asp.net Mvc 的机制就在这里,我们都知道,IIS里的线程池是有限的,每次的Client端请求,都会到线程池里取一个空闲的线程,如果主线程一直在”占用“线程池,

很快线程池就会被利用完啦。此时我们平时说的”吞吐量“的高低就是与此息息相关!当线程池被请求完后,再次有新的Client端请求,要会等待线程池的释放。

而mvc 就引用了控制器里异步方法的机制,原理就是让耗时的线程,直接返回,交给主线程,从而主线程会第一时间释放线程池的占用,而耗时的子线程完成时,将会在await标记从继续运行,

由此可以看出Mvc的异步将大大提高了应用程序的”吞吐量“。

至于具体的mvc异步编程机制与原理,网上一大把,也可以看看mvc的源代码,这里只简单的说下,本文的主旨await标记给异步带来的作用。



话题转回来:

那么我们何时在调用异步方法用await 作”标记“呢?

看看microsoft的经典例子

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • await和async在一般处理程序中的使用 - wolfy

    await和async在一般处理程序中的使用 - wolfy

    2015-12-24 11:00

  • 你眼中的async/await是什么样的? - richiezhang

    你眼中的async/await是什么样的? - richiezhang

    2015-12-05 11:22

  • NodeJs通过async/await处理异步 - Yika丶J

    NodeJs通过async/await处理异步 - Yika丶J

    2015-11-26 08:38

网友点评
p