python 中协程概念是从 3.4 版本增加的,但 3.4 版本采用是生成器实现,为了将协程和生成器的使用场景进行区分,使语义更加明确,在 python 3.5 中增加了
async
和
await
关键字,用于定义原生协程。
官方文档 都采用的是 run 方法。
官方案例
import asyncio
async def main():
print('hello')
await asyncio.sleep(1)
print('world')
asyncio.run(main())
接下来在查看一个完整的案例,并且结合 await 关键字。
import asyncio
import time
async def task1(x):
print("任务1")
await asyncio.sleep(2)
print("恢复任务1")
return x
async def task2(x):
print("任务2")
await asyncio.sleep(1)
print("恢复任务2")
return x
async def main():
start_time = time.perf_counter()
ret_1 = await task1(1)
ret_2 = await task2(2)
print("任务1 返回的值是", ret_1)
print("任务2 返回的值是", ret_2)
print("运行时间", time.perf_counter() - start_time)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
代码输出如下所示:
恢复任务1
恢复任务2
任务1 返回的值是 1
任务2 返回的值是 2
运行时间 2.99929154
上述代码创建了 3 个协程,其中 task1 和 task2 都放在了协程函数 main 中,I/O 操作通过 asyncio.sleep(1) 进行模拟,整个函数运行时间为 2.9999 秒,接近 3 秒,依旧是串行进行,如果希望修改为并发执行,将代码按照下述进行修改。
import asyncio
import time
async def task1(x):
print("任务1")
await asyncio.sleep(2)
print("恢复任务1")
return x
async def task2(x):
print("任务2")
await asyncio.sleep(1)
print("恢复任务2")
return x
async def main():
start_time = time.perf_counter()
ret_1,ret_2 = await asyncio.gather(task1(1),task2(2))
print("任务1 返回的值是", ret_1)
print("任务2 返回的值是", ret_2)
print("运行时间", time.perf_counter() - start_time)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
上述代码最大的变化是将 task1 和 task2 放到了 asyncio.gather() 中运行,此时代码输出时间明显变短。
恢复任务2
恢复任务1
任务1 返回的值是 1
任务2 返回的值是 2
运行时间 2.0005669480000003
asyncio.gather() 可以更换为 asyncio.wait() ,修改代码如下所示:
import asyncio
import time
async def task1(x):
print("任务1")
await asyncio.sleep(2)
print("恢复任务1")
return x
async def task2(x):
print("任务2")
await asyncio.sleep(1)
print("恢复任务2")
return x
async def main():
start_time = time.perf_counter(
)
done, pending = await asyncio.wait([task1(1), task2(2)])
print(done)
print(pending)
print("运行时间", time.perf_counter() - start_time)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
asyncio.wait() 返回一个元组,其中包含一个已经完成的任务集合,一个未完成任务的集合。
gather 和 wait 的区别
gather:需要所有任务都执行结束,如果任意一个协程函数崩溃了,都会抛异常,不会返回结果;
wait:可以定义函数返回的时机,可以设置为 FIRST_COMPLETED (第一个结束的), FIRST_EXCEPTION(第一个出现异常的), ALL_COMPLETED(全部执行完,默认的)。
done,pending = await asyncio.wait([task1(1),task2(2)],return_when=asyncio.tasks.FIRST_EXCEPTION)
cloudbbs@huaweicloud.com