1. await的作用

async def main():...                    # ①result = await xxx()   # ②...                    # ③result2 = await yyy()  # ④

await类似yield from,程序main()执行到await表达式处(②),main()会在②处阻塞,③④所在程序无法继续执行下去,直到xxx()执行完并返回结果。

xxx()执行完并返回结果后,返回结果赋值给变量result,然后main()继续执行下去。

await的作用就是阻塞调用方,并获取await xxx()中xxx的返回值。

await好像就是一种控制流程,下面if语句进行不严谨类比。

def main():..._r = Noneif True:        # ①_r = xxx()  # ②result = _r     # ③...             # ④

程序执行到①处,就会在if分支里面(②)执行,③处的程序只在执行②执行完了,才能继续。

await就类似于if True了,await xxx()中的xxx()就类似if语句里面逻辑了。

2. 如何在协程中使用await?

# 程序一import time
import asyncio
from collections import namedtupleResult = namedtuple('Result', 'name, result')async def factorial(name, n):f = 1for i in range(1, n + 1):f *= iawait asyncio.sleep(n)print(f'> {time.strftime("%X")} - ({name}) Factorial({n})={f}')return Result(name, f)async def main():print(f'=== start time: {time.strftime("%X")}')r = await factorial('fact-1', 3)print(f'Second time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')r = await factorial('fact-2', 5)print(f'=== Third time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')# if __name__ == '__main__':  在非Jupyter上应该使用这个
#     asyncio.run(main())
await main()  # 在Jupyter上执行

执行结果:

=== start time: 22:57:35
> 22:57:38 - (fact-1) Factorial(3)=6
Second time:22:57:38, Result: <fact-1, 6>
> 22:57:43 - (fact-2) Factorial(5)=120
=== Third time:22:57:43, Result: <fact-2, 120>
# 程序二async def main():print(f'=== start time: {time.strftime("%X")}')r = await asyncio.create_task(factorial('fact-1', 3))print(f'Second time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')r = await asyncio.create_task(factorial('fact-2', 5))print(f'=== Third time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')await main()

执行结果:

=== start time: 22:40:07
> 22:40:10 - (fact-1) Factorial(3)=6
Second time:22:40:10, Result: <fact-1, 6>
> 22:40:15 - (fact-2) Factorial(5)=120
=== Third time:22:40:15, Result: <fact-2, 120>

程序一和程序二的执行结果都说明了await 阻塞作用,只有执行完await表达式里的协程factorial()main()函数才能继续执行下面的代码。

上面两个程序,去掉asyncawait 关键字,执行的效果同平时写的同步代码是一样的。

所以说,如果不是为了并发,使用async/await的效果等同于用同步方式写的,还增加代码的理解难度。

程序二中,asyncio.create_task()方法是可以用来并发运行的。

3. await在运行并发任务上的如何使用

await关键字使用上的疑惑和纠结,就是我要并发,但await这个关键字我该怎么使用?在何处用?

想要并发,但await的作用却是阻塞程序,并发和await这两个就是矛盾体。没写好,就写成同步去了… 类似程序二,明明create_task()用来并发,执行结果却没效果。

await在何处使用关乎并发的效果。

# 程序三async def main():print(f'=== start time: {time.strftime("%X")}')task1 = asyncio.create_task(factorial('fact-1', 3))  # ①task2 = asyncio.create_task(factorial('fact-2', 7))  # ②print('await start ...')  # ③r = await task2  # ④print(f'=== Second time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')r = await task1 # ⑤print(f'=== Third time:{time.strftime("%X")}, Result: <{r.name}, {r.result}>')await main()

执行结果:

=== start time: 22:44:25
await start ...
> 22:44:28 - (fact-1) Factorial(3)=6
> 22:44:32 - (fact-2) Factorial(7)=5040
=== Second time:22:44:32, Result: <fact-2, 5040>
=== Third time:22:44:32, Result: <fact-1, 6>

程序三跟程序二不同点在于create_task()处没有添加await去阻塞。

create_task()会将协程封装成一个Task,并返回一个Task对象。该方法不会阻塞main()程序,所以,①②处的代码瞬间就执行完了,并继续执行下去。

create_task()创建的Task,会自动添加到事件循环上,通俗的说,事件循环就是调度Task并发的,但何时并发,我们是无法控制的。

所以,使用create_task()来并发是“隐性”的。这个隐性是指,我们没有类似多线程那样有很明确的start()启动并发。而是执行了create_task()后就算是启动了并发。

当程序执行到③时,①②创建的两个任务可能在并发执行了(如果没有,可能过一会儿就并发了)。

程序四的执行情况就说明了create_task()执行后在并发执行

现在疑惑,既然并发执行了,为什么需要在④和⑤上添加await?

用程序五来说明

# 程序四
async def main():"""task是自行调度的,即使没有await task1也是在执行的。"""print(f'\n=== start time: {time.strftime("%X")}')asyncio.create_task(factorial('fact-1', 3))asyncio.create_task(factorial('fact-2', 7))print('await start ...')await asyncio.sleep(20)print(f'=== End time: {time.strftime("%X")}')await main()

执行结果:

=== start time: 22:44:43
await start ...
> 22:44:46 - (fact-1) Factorial(3)=6
> 22:44:50 - (fact-2) Factorial(7)=5040
=== End time: 22:45:03
# 程序五
async def main():"""create_task()会返回一个task对象,task会被事件调度器自动处理,该方法不会阻塞,所以返回task对象后会执行执行。"""print(f'=== start time: {time.strftime("%X")}')asyncio.create_task(factorial('fact-1', 3))  # ①asyncio.create_task(factorial('fact-2', 7))  # ②print('await start ...')await asyncio.sleep(6)  # ③print(f'=== End time: {time.strftime("%X")}')await main()

执行结果:

=== start time: 22:45:03
await start ...
> 22:45:06 - (fact-1) Factorial(3)=6
=== End time: 22:45:09
> 22:45:10 - (fact-2) Factorial(7)=5040

①处是个耗时3s的操作,②是耗时7s的操作。③是用来模拟main()这个主线程执行完要6s。而当主线程执行完后就关闭了。

在主线程存活这6s内,开启的协程①是可以执行结束的,而协程②要7s才能执行完。故当主线程关闭了,里面还没有执行完的任务(这里是②)也一同被清了。类似电脑主机关机了,里面还在跑的程序也被强制关停了。

所以,在并发上,await是可以用来保证并发任务执行结束,同时接收并发任务返回值的,如程序三。

如果不关心是否要执行完,确实是可以不用await的。比如在一个死循环里或者不停服的代码里并发。

所以,await放在程序的位置是很关键的。如果要并发多个任务,写完多个create_task(),如果后面的代码跟并发返回结果无关联,最好将await放在其他代码后面。不然直接create_task()就await了,导致后面的代码无法执行,这样效果就不甚理想。

async main():...task1 = asyncio.create_task(coro())   # ①task2 = asyncio.create_task(coro())   # ②......   # 这里表示其他要执行的逻辑代码  ③r = await task1   # await不直接放在②后面,而放在③后面,这样③处的执行才不被阻塞。r2 = await task2

同理,在使用asyncio.as_completed()来开启并发,如何使用await,是跟使用create_task()思路一样的。

如果不需要返回值并且不关心是否正常执行完,可以不使用await,如果要用且在不影响其他逻辑代码情况,尽量对任务并发的结果放在后面处理。

但如果使用asyncio.gather()asyncio.wait()来并发操作,因为这两个方法语法原因,都是需要使用到await的。

所以要用asyncio.gather()asyncio.wait(),不用想,在它们前面加await就好。

asyncio中await关键字如何使用相关推荐

  1. 浅谈async、await关键字 = 深谈async、await关键字

    前言 之前写过有关异步的文章,对这方面一直比较弱,感觉还是不太理解,于是会花点时间去好好学习这一块,我们由浅入深,文中若有叙述不稳妥之处,还请批评指正. 话题 (1)是不是将方法用async关键字标识 ...

  2. Python3 中的 asyncio async await 概念(实例)(ValueError: too many file descriptors in select())

    代码实例 import timedef demo4():"""这是最终我们想要的实现."""import asyncio # 引入 asyn ...

  3. 【Flutter】Future 异步编程 ( 简介 | then 方法 | 异常捕获 | async、await 关键字 | whenComplete 方法 | timeout 方法 )

    文章目录 一.Future 简介 二.Future.then 使用 三.Future 异常捕获 四.Dart 练习网站 五.async.await 关键字 六.whenComplete 方法 七.ti ...

  4. java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题

    先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...

  5. 如何保证执行异步方法时不会遗漏 await 关键字

    前言 在.NET Core 中已经广泛使用异步编程,代码中充斥着大量的 async 和 await 关键字. 但有时候,调用一个异步方法时可能会忘了写 await. 这会造成什么问题呢? 问题重现 示 ...

  6. C#中await/async闲说

    自从C#5.0增加异步编程之后,异步编程越来越简单,async和await用的地方越来越多,越来越好用,只要用异步的地方都是一连串的异步,如果想要异步编程的时候,需要从底层开始编写,这样后边使用的时候 ...

  7. 为什么我们要使用Async、Await关键字

    前不久,在工作中由于默认(xihuan)使用Async.Await关键字受到了很多质问,所以由此引发这篇博文"为什么我们要用Async/Await关键字",请听下面分解: Asyn ...

  8. c#进阶(7)—— 异步编程基础(async 和 await 关键字)

    async 和 await 关键字只是编译器功能,编译器会用Task类创建代码. 返 回值是一个Task,这种返回新线程的方法虽然可以提高系统的响应能力,但是多线程取值会给编码带来不便,所以新出的关键 ...

  9. 【Java_基础】Java中Native关键字的作用

    本篇博文转载与:Java中Native关键字的作用 转载于:https://www.cnblogs.com/leiblog/p/10529056.html

最新文章

  1. create-react-app my-app 报错解决方法
  2. oracle 里 符号 || 表示什么意思??
  3. 20160331数据文件offline与open resetlogs2
  4. U-Boot如何向内核传递Flash的分区信息
  5. oracle 存储过程获取当前日期
  6. php项目数据库控制器代码_如何为大型代码库组织Express控制器
  7. c语言while跳不出来,这个while循环终止了却跳不出来为什么
  8. guassdb200 single node deployment
  9. 工作开不开心,都在钱上
  10. XMLHttpRequest2 异步 ajax
  11. A guess 解题报告
  12. Word如何任意页开始插入页码
  13. java连接微信发送给好友信息,微信消息转发以及给指定好友发送消息
  14. 中国传统节日简介、由来、习俗
  15. 内卷老员工之java内存模型的happens-before原则
  16. 163/qq邮箱设置POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
  17. 非常可乐(HDU-1495)
  18. java基本编程结构
  19. 蓝牙模块的TX连接51的RX,发送指令
  20. STM32F051——USART

热门文章

  1. 【ng2】运行 ng serve --open 时报错
  2. 基于OpenPose的人体姿态检测(非常好)
  3. 火影忍者手游打秘境服务器响应超时,火影忍者手游秘境全攻略 新版秘境打法细节说明...
  4. 电脑装机之后耳机有杂音,原因及解决方案
  5. 记一次放内存切图|BytesIO|PIL库
  6. 简单粗暴的方式解决eclipse下安装STS失败的问题
  7. 记忆力测试软件 知乎,知乎干货分享:如何提升你的记忆力
  8. T703软件测试简历,OPPO Finder x907 系统应用及性能测试
  9. 关于MySQL自增id不连续问题
  10. python3中有urllib2吗_python3没有urllib2该怎么处理?