当前位置: 首页 > news >正文

python3协程学习-async,await

参考文档:

https://cloud.tencent.com/developer/article/2002528

https://cloud.tencent.com/developer/article/2480588?policyId=1004

什么是协程

https://docs.python.org/3.11/glossary.html#term-coroutine

协程:Coroutines are a more generalized form of subroutines. Subroutines are entered at one point and exited at another point. Coroutines can be entered, exited, and resumed at many different points. They can be implemented with the async def statement. See also PEP 492.  协程是一种更通用的子进程形式,子进程就是从一点进入(调用)到退出(返回结果),协程可以在不同的点进入和退出并恢复。使用async def语句实现。

协程函数: A function which returns a coroutine object. 返回一个协程对象的函数

洗衣机例子:

多线程:
假设有1个洗衣房,里面有10台洗衣机,有一个洗衣工在负责这10台洗衣机。那么洗衣房就相当于1个进程,洗衣工就相当1个线程。如果有10个洗衣工,就相当于10个线程,1个进程是可以开多线程的。
那么协程呢? 先不急。大家都知道,洗衣机洗衣服是需要等待时间的,如果10个洗衣工,1人负责1台洗衣机,这样效率肯定会提高,但是不觉得浪费资源吗?明明1个人能做的事,却要10个人来做。
只是把衣服放进去,打开开关,就没事做了,等衣服洗好再拿出来就可以了。就算很多人来洗衣服,1个人也足以应付了,开好第一台洗衣机,在等待的时候去开第二台洗衣机,再开第三台,……直到有衣服洗好了,就回来把衣服取出来, 接着再取另一台的(哪台洗好先就取哪台,所以协程是无序的)。这就是计算机的协程!洗衣机就是执行的方法。”

协程,又称微线程。 协程的作用是在执行函数A时可以随时中断去执行函数B,然后中断函数B继续执行函数A(可以自由切换)。 但这一过程并不是函数调用,这一整个过程看似像多线程,然而协程只有一个线程执行。

协程很适合处理IO密集型程序的效率问题。协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,因此对于CPU密集型程序协程需要和多进程配合。

 https://docs.python.org/3.11/glossary.html#term-coroutine
 

同步和异步

 

async/await

async: 当函数被 async 关键字修饰后,调用该函数不会直接执行函数体,而是返回一个协程对象

await: Suspend the execution of coroutine on an awaitable object. Can only be used inside a coroutine function. https://peps.python.org/pep-0492/#await-expression, 仅可以使用在协程函数中,等待一个可等待对象执行完。

await 后面必须要是一个可等待对象

  • await + 可等待对象(协程对象,Future,Task对象(IO等待))
  • 等待到对象的返回结果,才会继续执行后续代码
错误等待会报错
    await time.sleep(2)  # 第一台洗衣机,
TypeError: object NoneType can't be used in 'await' expression

  

 

单线程执行

所有函数都是串行调用。

 

import timedef washing1():print('washer1 start at {}'.format(time.time()))time.sleep(2)  # 第一台洗衣机,print('washer1 finished at {}'.format(time.time()))  # 洗完了def washing2():print('washer2 start at {}'.format(time.time()))time.sleep(3)print('washer2 finished at {}'.format(time.time()))def washing3():print('washer3 start at {}'.format(time.time()))time.sleep(6)print('washer3 finished at {}'.format(time.time()))if __name__ == '__main__':start_time = time.time()print("main start at {}".format(time.time()))washing1()washing2()washing3()print("main finished at {}".format(time.time()))end_time = time.time()print('总共耗时:{}'.format(end_time-start_time))

# 可以看到总耗时 2 + 3 + 6 = 11 秒 # main start at 1757666672.326777 # washer1 start at 1757666672.3268082 # washer1 finished at 1757666674.328899 # washer2 start at 1757666674.3289628 # washer2 finished at 1757666677.3320591 # washer3 start at 1757666677.3321292 # washer3 finished at 1757666683.3382258 # main finished at 1757666683.3382993 # 总共耗时:11.01153826713562

  

 协程(异步)

协程(coroutines)通过async/await语法进行声明,通过async装饰了的函数不能直接执行,因为函数此时返回的是一个协程对象。

import timeasync def washer1():time.sleep(3)  # 第一台洗衣机,print('washer1 finished')  # 洗完了res = washer1()
print(res)   # <coroutine object washer1 at 0x7f0b1918b0d0>

 

协程需要asyncio去执行

import time
import asyncio
async def washer1():print('washer1 start at {}'.format(time.time()))time.sleep(2) print('washer1 finished at {}'.format(time.time())) # 洗完了if __name__ == '__main__':res = washer1()asyncio.run(res)print(res) # <coroutine object washer1 at 0x7f0b1918b0d0> # washer1 start at 1757667121.1676168 # washer1 finished at 1757667123.169721 # <coroutine object washer1 at 0x7f7f7591b140>

  

在上面的例子中只执行了一个任务,所以没有看出协程的优势,尝试执行多个任务。

import time
import asyncioasync def washing1():print('washer1 start at {}'.format(time.time()))time.sleep(2)  # 第一台洗衣机,print('washer1 finished at {}'.format(time.time()))  # 洗完了async def washing2():print('washer2 start at {}'.format(time.time()))time.sleep(3)print('washer2 finished at {}'.format(time.time()))async def washing3():print('washer3 start at {}'.format(time.time()))time.sleep(6)print('washer3 finished at {}'.format(time.time()))async def main():start_time = time.time()print("main start at {}".format(time.time()))# task1 = asyncio.create_task(washing1())# task2 = asyncio.create_task(washing2())# task3 = asyncio.create_task(washing3())# await task1# await task2# await task3# 多个任务的时候如下优化loop = asyncio.get_running_loop()tasks = [loop.create_task(washing1()), loop.create_task(washing2()), loop.create_task(washing3())]await asyncio.gather(*tasks)# tasks = [washing1(),washing2(),washing3()]   # fixme why? # loop.run_until_complete(asyncio.wait(tasks))print("main finished at {}".format(time.time()))  # fixme why?end_time = time.time()print('总共耗时:{}'.format(end_time-start_time))if __name__ == '__main__':asyncio.run(main())
# main start at 1757668470.8842885
# washer1 start at 1757668470.8843513
# washer1 finished at 1757668472.8861766
# washer2 start at 1757668472.8862946
# washer2 finished at 1757668475.8894012
# washer3 start at 1757668475.8895106
# washer3 finished at 1757668481.8956199
# main finished at 1757668481.8958285
# 总共耗时:11.011565685272217

  

这里采用了异步执行,仍然使用了11s,这个是因为time.sleep是阻塞式的,并没有释放线程的控制权(不够准确),所以函数仍然串行执行完了。替换time.sleep 为asyncio.sleep(2)

import time
import asyncioasync def washing1():print('washer1 start at {}'.format(time.time()))# time.sleep(2)  # 第一台洗衣机,  await不能等待time,会在回调的时候报错await asyncio.sleep(2)print('washer1 finished at {}'.format(time.time()))  # 洗完了async def washing2():print('washer2 start at {}'.format(time.time()))await asyncio.sleep(3)print('washer2 finished at {}'.format(time.time()))async def washing3():print('washer3 start at {}'.format(time.time()))await asyncio.sleep(6)print('washer3 finished at {}'.format(time.time()))async def main():start_time = time.time()print("main start at {}".format(time.time()))# 多个任务的时候如下优化loop = asyncio.get_running_loop()tasks = [loop.create_task(washing1()), loop.create_task(washing2()), loop.create_task(washing3())]await asyncio.gather(*tasks)# tasks = [washing1(),washing2(),washing3()]   # fixme why? # loop.run_until_complete(asyncio.wait(tasks))print("main finished at {}".format(time.time()))  # fixme why?end_time = time.time()print('总共耗时:{}'.format(end_time-start_time))if __name__ == '__main__':asyncio.run(main())
# main start at 1757668647.4589832
# washer1 start at 1757668647.4590447
# washer2 start at 1757668647.4590783
# washer3 start at 1757668647.4591055
# washer1 finished at 1757668649.4613025
# washer2 finished at 1757668650.4606535
# washer3 finished at 1757668653.462307
# main finished at 1757668653.462472
# 总共耗时:6.00351619720459

 

再看下更复杂的场景,我们把洗衣服拆分为 洗衣服和加衣服进去的两个动作,假如我们使用两个洗衣机同时洗衣服和袜子。看下效果

import time
import asyncioasync def add_stocks():print('get_stocks start at {}'.format(time.time()))await asyncio.sleep(2)print('get_stocks finished at {}'.format(time.time()))async def add_clothes():print('add_clothes start at {}'.format(time.time()))await asyncio.sleep(3)print('add_clothes finished at {}'.format(time.time()))async def washing_stocks():print('washer1 add_stocks start at {}'.format(time.time()))await add_stocks()print('washer1 add_stocks finished at {} and start washing'.format(time.time()))# time.sleep(2)  # 第一台洗衣机,await asyncio.sleep(2)print('washer1 finished at {}'.format(time.time()))  # 洗完了async def washing2():print('washer2 add_clothes start at {}'.format(time.time()))await add_clothes()print('washer2 add_clothes finished at {} and start washing'.format(time.time()))await asyncio.sleep(3)print('washer2 finished at {}'.format(time.time()))async def main():start_time = time.time()print("main start at {}".format(time.time()))# 多个任务的时候如下优化loop = asyncio.get_running_loop()tasks = [loop.create_task(washing_stocks()), loop.create_task(washing2())]await asyncio.gather(*tasks)print("main finished at {}".format(time.time())) end_time = time.time()print('总共耗时:{}'.format(end_time-start_time))if __name__ == '__main__':asyncio.run(main())
# main start at 1757669126.4952745
# washer1 add_stocks start at 1757669126.4953442
# get_stocks start at 1757669126.4953556
# washer2 add_clothes start at 1757669126.4953816  
# add_clothes start at 1757669126.4953878   # 这里可以看到基本都是两个washer基本同时启动,且同时开始加待洗的东西 
# get_stocks finished at 1757669128.4976122  # 第一个加入的要洗的袜子结束(耗时2s)开始洗
# washer1 add_stocks finished at 1757669128.4976714 and start washing
# add_clothes finished at 1757669129.4969563
# washer2 add_clothes finished at 1757669129.4970083 and start washing
# washer1 finished at 1757669130.5003135
# washer2 finished at 1757669132.498279
# main finished at 1757669132.4984148
# 总共耗时:6.0031867027282715  # 总共耗时 3+3 =6

  

https://cloud.tencent.com/developer/article/2480588?policyId=1004

 添加超时

 

 

取消任务

http://www.wxhsa.cn/company.asp?id=2122

相关文章:

  • 猫树分治
  • Rust太难了。。。。。。。
  • AI导航生成寻路点-FindPathToLocationSynchronously
  • cache写策略
  • 个人微信开发
  • C++之std::is_trivially_copyable
  • PostgreSQL技术大讲堂 - 第104讲:PostgreSQL分区表应用实践
  • redis实现缓存1-添加商户缓存
  • qemu的外部快照实现原理
  • Springboot 集成 飞书群消息
  • 最新爆料:GitHub Copilot全面推出OpenAI GPT-5 和 GPT-5 mini!
  • netstat 命令查看端口状态详解
  • 智聘无界:AI 破解全球化招聘合规、成本与人才匹配难题的实践路径
  • Nature | 本周最新文献速递
  • Flink 与Flink可视化平台StreamPark教程(CDC功能)
  • GAS_Aura-Setting Up Auto Running
  • Ubuntu 24.04 LTS 登录用户和密码忘记找回方法
  • 错排问题
  • 源码调试-带你了解下车牌识别的深度学习模型-LPRNet
  • 仓储物流业务字段(一)
  • ubuntu 24.04部署mysql8.0.41(glibc2.28)
  • cmakelist文件中常见语句的含义
  • charles破解-在线生成激活码
  • 微信个人号开发
  • 内部排序-直接插入排序冒泡排序快速排序对比
  • STM32读写EEPROM
  • OpenStack Nova 创建虚拟机
  • AI革命2025:新一代人力资源管理系统十大标杆产品评测
  • 企业HR系统选型全指南:百人初创到万人集团的数字化方案与实施路径
  • C++ auto关键字