一文读懂Asyncio
什么是Asyncio
asyncio 是用来编写并发代码的库,使用async/await语法。
asyncio 被用作多个提供高性能 Python 异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。
asyncio 往往是构建 IO 密集型和高层级结构化网络代码的最佳选择。
线程和协程
协程是一种比线程更加轻量级的存在
线程:把需要执行的任务比作汽车,线程就像一条单行且只有一条道的高速公路,只有等前一辆车到达终点后面的车才能出发,如果其中一辆出了事情停在了路上,那么这俩车后面的车就只能原地等待直到它恢复并到达终点才能继续上路。
协程:把需要执行的任务比作汽车,协程就像一条带应急车道的高速公路,如果汽车在中途出了问题就可以直接到一边的应急车道停下处理问题,下一辆车可以直接上路,简单来说就是可以通过程序控制哪辆车行驶,哪辆车在应急车道休息。
同步和异步
同步:意味着有序
异步:意味着无序同步必须一件事情结束之后再进行下一件事,异步是可以在一件事情没结束就去处理另外一件事情了。
定义协程函数
# work()函数就是一个协程
async def work(x):
peint(x)
await asyncio.sleep(x)
# 验证协程函数
print(asyncio.iscoroutine(work(1)))
协程对象
协程函数() 得到的对象
注意: res = work()创建协程对象,函数内部代码不会执行。如果要运行协程函数内部代码,必须要将协程对象交给事件循环来处理。
# work()函数就是一个协程
async def work(x):
peint(x)
await asyncio.sleep(x)
res = work()
loop = asyncio.get_event_loop()
loop.run_until_complete(res)
运行协程函数
调用协程函数,协程并不会开始运行,只是返回一个协程对象,可以通过 asyncio.iscoroutine 来验证:
print(asyncio.iscoroutine(work(3)))
# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()
# run_until_complete 的参数是一个 future,但是我们这里传给它的却是协程对象
loop.run_until_complete(asyncio.ensure_future(do_some_work(3)))
多个协程
实际项目中,往往有多个协程,同时在一个 loop 里运行。为了把多个协程交给 loop,需要借助 asyncio.gather 函数。
loop.run_until_complete(asyncio.gather(work(1), work(3)))
# 这两个协程是并发运行的,所以等待的时间不是 1 + 3 = 4 秒,而是以耗时较长的那个协程为准
# gather 起聚合的作用,把多个 futures 包装成单个 future,因为 loop.run_until_complete 只接受单个 future。
await关键字
await后跟可等待对象,可等待对象包括协程对象、Future和Task对象,这些都是IO等待。等IO操作完成之后再继续往下执行,当前协程(任务)挂起时,事件循环可以执行其他协程(任务)。
同一个协程任务中,多个await,会依次等待等待对象执行完成;不同协程任务中,遇到await会交替执行。
案例1:
import asyncio
async def func1():
print(1)
await asyncio.sleep(2)
print(2)
return "func1"
async def test1():
print("执行协程函数test1内部代码")
res = await func1()
print("IO请求结束test1,结果为: ", res)
res = await func1()
print("IO请求结束test1,结果为: ", res)
tasks = [
asyncio.ensure_future(test1())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
案例2
import asyncio
async def func1():
print(1)
await asyncio.sleep(2)
print(2)
return "func1"
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
return "func2"
async def test1():
print("执行协程函数test1内部代码")
res = await func1()
print("IO请求结束test1,结果为: ", res)
async def test2():
print("执行协程函数test2内部代码")
res = await func2()
print("IO请求结束test2,结果为: ", res)
tasks = [
asyncio.ensure_future(test1()),
asyncio.ensure_future(test2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
task对象
Task被用来在事件循环中添加多个任务的。
当协程包装到具有asyncio.create_task()等函数的任务中时,会自动地添加到事件循环中等待被调度执行。除了使用asynico.create_task()函数以外,还可以用底层级的loop.create_create_task()或ensure_future()函数。
案例1
import asyncio
async def func1(n):
print(n)
await asyncio.sleep(2)
print(n)
return f"func{n}"
async def main():
print('开始')
# 自动加入到事件循环里
task1 = asyncio.create_task(func1(1))
# 自动加入到事件循环里
task2 = asyncio.create_task(func1(2))
print('结束')
res1 = await task1
res2 = await task2
print(res1, res2)
asyncio.run(main())
案例2
import asyncio
async def func1():
print(1)
await asyncio.sleep(2)
print(2)
return "func1"
async def main():
print('开始')
task_list = [
# 自动加入到事件循环里
asyncio.create_task(func1(), name='task1'),
# 自动加入到事件循环里
asyncio.create_task(func1(), name='task2')
]
print('结束')
done, pending = await asyncio.wait(task_list, timeout=None)
print(done)
asyncio.run(main())