(本文主要参照Python在线文档进行学习)
0.前言
进程有自己独立的堆和栈,由操作系统调度;线程有独立的栈,共享堆空间,标准的线程也是由操作系统调度;协程和线程一样共享堆不共享栈,但是是由程序员在协程的代码里显示调度。协程避免了无意义的调度,进而提升性能,但也增加了程序员的责任。
1.初探
asyncio是一个使用 async / await 语法编写并发代码的库,在Python 3.4 引入,直接内置了对异步IO的支持,并在后来的版本中不断完善。早期基于生成器来实现协程,但是在 Python 3.8 开始不推荐使用该方式,并将在 3.10 移除。目前标准的是使用 async / await 语法来实现协程。
要实际运行协程,asyncio 提供了三种主要机制:
a.使用 asyncio.run() 函数来运行顶层入口点
(运行代码需要 Python3.7 ;要注意的是,直接调用 func() 并不能执行)
import asyncio
import time
async def func():
print('func in'+time.strftime('%X'))
await asyncio.sleep(3)
print('func out'+time.strftime('%X'))
print('begin'+time.strftime('%X'))
asyncio.run(func())
print('end'+time.strftime('%X'))
b.等待协程
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {time.strftime('%X')}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
c.用asyncio.create_task() 函数把协程作为异步任务来同时执行
(修改上个示例的 main 函数)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello'))
task2 = asyncio.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
2.可等待的对象 awaitable
如果可以在 await 表达式中使用,则这个对象是 awaitable 的。可等待的对象主要有三种:协程coroutines,任务Tasks,期望Futures。
a.协程coroutines
Python 协程是可等待的,并且可以等待自其他协程:
import asyncio
async def nested():
return 42
async def main():
# 一个协程对象创建了但是没有被等待,不会执行
print(nested())
# 输出 "42".
print(await nested())
asyncio.run(main())
协程相关的两个重要概念:协程函数,通过 async def 定义的函数;协程对象,调用协程函数返回的对象。
b.任务Tasks
任务可以用来调度协程并发执行。当使用 asyncio.create_task() 打包一个协程,该协程很快会自动计划运行。
import asyncio
async def nested():
return 42
async def main():
# nested() 与 main() 会同时运行
task = asyncio.create_task(nested())
# "task" 现在可用于取消 "nested()", 或是等待其完成
await task
asyncio.run(main())
c.期望Futures
Futures 是特殊的低级可等待对象,代表异步操作的最终结果。当 await 一个 Futures 时,协程将等待直到 Futures 在别的地方完成。一般不需要在应用中创建 Futures 对象。
3.部分API参考
a.运行一个异步程序
asyncio.run(coro, *, debug = False )
执行协程 coro 并返回结果。它应该用作 asyncio 程序的主要入口点,理想情况下应该只调用一次。
b.创建任务
asyncio.create_task(coro, *, name = None )
将协程 coro 包装为一个 Task 并调度执行,返回一个 Task 对象。
c.休眠
asyncio.sleep(delay, result=None, *, loop=None)
阻塞等待 delay 秒,sleep 挂起当前任务,允许其他任务运行。
d.同时运行
asyncio.gather(*aws, loop=None, return_exceptions=False)
并发执行 aws 列表中的可等待对象。如果 aws 中有任何可等待的协程,将自动作为一个 Task 调度。
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number + 1):
print(f"Task {name}: Compute factorial({i})...")
await asyncio.sleep(1)
f *= i
print(f"Task {name}: factorial({number}) = {f}")
async def main():
# Schedule three calls *concurrently*:
await asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
)
asyncio.run(main())
# Expected output:
#
# Task A: Compute factorial(2)...
# Task B: Compute factorial(2)...
# Task C: Compute factorial(2)...
# Task A: factorial(2) = 2
# Task B: Compute factorial(3)...
# Task C: Compute factorial(3)...
# Task B: factorial(3) = 6
# Task C: Compute factorial(4)...
# Task C: factorial(4) = 24
e.超时等待
asyncio.wait_for(aw, timeout, *, loop=None)
aw 是一个协程,如果 timeout 秒数为 None,将会阻塞直到完成。
async def eternity():
# Sleep for one hour
await asyncio.sleep(3600)
print('yay!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
# Expected output:
#
# timeout!
协程相关的接口还是挺多的,这里只列举了部分,详情可以参照书籍或文档
4.参考:
Python在线文档:https://docs.python.org/3/library/asyncio-task.html
百度:https://wenku.baidu.com/view/a52ac722a22d7375a417866fb84ae45c3b35c24e.html
博客:https://www.jianshu.com/p/e38ee24dd004
博客:https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824
来源:CSDN
作者:龚建波
链接:https://blog.csdn.net/gongjianbo1992/article/details/103752661