异步方法介绍(二)
上次讲异步的时候讲了一些简单的异步实现,这一次稍微深入一下。
上次使用低级的事件循环来监视任务,其实asyncio
模块里面提供了高级的run
函数可以直接开始一个事件循环,不用手动调用。不知道小伙伴们注意到上次wait
函数返回的对象没有,返回的对象都是Future
类实例,这个类就是将来要做的任务,可以通过asyncio.create_task
显式地创建一个Future
对象,这个对象有很多方法。
源代码如下:
""" 异步的高级操作
使用run函数来操作协程
asyncio.run(async_func(...))
创建任务
t = asyncio.create_task(async_func(...))
await t
the statistics of this file:
lines(count) understand_level(h/m/l) classes(count) functions(count) fields(count)
000000000092 ----------------------m 00000000000000 0000000000000004 ~~~~~~~~~~~~~
"""
import time
import asyncio
__author__ = '与C同行'
async def wait_noblock_n_second(n, verbose):
print('进入wait_noblock_n_second函数')
await asyncio.sleep(n)
print(f'{verbose}离开wait_noblock_n_second函数')
return n+1
async def main_block():
"""
协程没有包装成Future或者Task类,将阻塞
"""
print('协程开始')
time_start = time.time()
task1 = wait_noblock_n_second(1, '1s之后')
task2 = wait_noblock_n_second(2, '2s之后')
await task1
await task2
time_end = time.time()
print(f'运行耗时:{time_end-time_start}s')
print('协程结束')
async def main_noblock():
"""
协程包装成Future或者Task类,不会阻塞
"""
print('协程开始')
time_start = time.time()
task1 = asyncio.create_task(wait_noblock_n_second(1, '1s之后'))
task2 = asyncio.create_task(wait_noblock_n_second(2, '2s之后'))
await task1
await task2
time_end = time.time()
print(f'运行耗时:{time_end-time_start}s')
print('协程结束')
async def main_noblock_gather():
"""
使用gather来等待协程完成,并返回有序的结果
"""
print('协程开始')
time_start = time.time()
task1 = asyncio.create_task(wait_noblock_n_second(3, '3s之后'))
task2 = asyncio.create_task(wait_noblock_n_second(2, '2s之后'))
task3 = asyncio.create_task(wait_noblock_n_second(1, '1s之后'))
tasks = [task1, task2, task3]
results = await asyncio.gather(*tasks)
time_end = time.time()
print(f'所有协程运行耗时:{time_end-time_start}s')
print('各个协程gather之后的结果:')
for item in results:
print(item)
print('协程结束')
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('使用run函数来运行协程')
asyncio.run(main_block())
asyncio.run(main_noblock())
asyncio.run(main_noblock_gather())
print()
print('显式调用轮询器调度协程')
loop = asyncio.get_event_loop()
loop.run_until_complete(main_block())
loop.run_until_complete(main_noblock())
loop.run_until_complete(main_noblock_gather())
loop.close()
这一次的异步方法起点和上次不一样了,这一次是从一个异步函数进去的,上次是一次启动所有的异步函数。
先看一下不创建Future
对象的异步方法,这里使用run
调用一个异步函数,隐式地调用事件循环器:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('使用run函数来运行协程')
asyncio.run(main_block())
结果如下:
可以看到不创建Future
对象,即使使用await
语句等待异步函数也会阻塞程序。
使用Future
对象:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('使用run函数来运行协程')
asyncio.run(main_noblock())
结果如下:
使用Future
对象之后就不会阻塞程序了。
接下来使用gather
来收集任务:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('使用run函数来运行协程')
asyncio.run(main_noblock_gather())
结果如下:
现在使用显式的事件循环器来监视异步函数:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('显式调用轮询器调度协程')
loop = asyncio.get_event_loop()
loop.run_until_complete(main_block())
loop.close()
结果如下:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('显式调用轮询器调度协程')
loop = asyncio.get_event_loop()
loop.run_until_complete(main_noblock())
loop.close()
结果如下:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
print('显式调用轮询器调度协程')
loop = asyncio.get_event_loop()
loop.run_until_complete(main_noblock_gather())
loop.close()
结果如下:
使用run
运行异步函数与使用事件循环器运行异步函数的结果是一致的。
总结:在异步函数中有多个异步函数调用时要创建任务,否则该异步调用函数将会阻塞程序。
喜欢python的朋友可以关注微信公众号“与C同行”,里面有更多python技巧。