异步方法介绍(三)

    技术2022-07-11  104

    异步方法介绍(三)

    异步方法最重要的内容是创建一个将来任务,这个将来任务由事件循环器来处理。创建任务使用asyncio.create_task,任务创建好了之后将任务集合交给事件循环器,并且等待完成任务。了解了这个过程,使用异步来完成某些任务完全没有问题,但是,有时候任务创建好了之后又不想完成这个任务,这个时候怎么办呢?有些暴脾气的人说了,直接把这个任务的源代码删掉不就行了,这个小问题还能难倒我,这样确实可以,但是,我们可以掌握一个温柔点的方法,让自己不那么粗俗,任务创建好了之后是可以取消的,所以没必要删除源码。

    使用task.cancel可以取消任务,但是取消任务是有限制的,并不是在什么情况下都能取消任务的。看一下取消任务的要求:

    使用Task.cancel()取消任务 wait取消任务没有问题 gather取消任务会引发asyncio.CancelledError异常 as_completed取消任务会引发asyncio.CancelledError异常

    源码如下:

    """ 取消部分协程 使用Task.cancel()取消任务 wait取消任务没有问题 gather取消任务会引发asyncio.CancelledError异常 as_completed取消任务会引发asyncio.CancelledError异常 the statistics of this file: lines(count) understand_level(h/m/l) classes(count) functions(count) fields(count) 000000000094 ----------------------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_wait_cancel(): """ wait取消部分协程不会出现异常 """ 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之后')) task3 = asyncio.create_task(wait_noblock_n_second(3, '3s之后')) task2.cancel() tasks = [task1, task2, task3] # 运行超过两秒就不运行了 done, pending = await asyncio.wait(tasks, timeout=2) time_end = time.time() print(f'运行耗时:{time_end-time_start}s') print(done) print(pending) print('协程结束') async def main_gather_cancel(): """ gather取消部分协程引发CancelledError异常 """ 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之后')) task3 = asyncio.create_task(wait_noblock_n_second(3, '3s之后')) task2.cancel() tasks = [task1, task2, task3] try: results = await asyncio.gather(*tasks) except asyncio.CancelledError: print('取消任务task2导致gather失败') results = None time_end = time.time() print(f'运行耗时:{time_end-time_start}s') print(results) print('协程结束') async def main_as_completed(): """ as_completed不能取消协程,否则引发CancelledError异常 """ print('协程开始') time_start = time.time() task1 = asyncio.create_task(wait_noblock_n_second(4, '4s之后')) task2 = asyncio.create_task(wait_noblock_n_second(2, '2s之后')) task3 = asyncio.create_task(wait_noblock_n_second(3, '3s之后')) # 不能取消任务,否则引发异常 # # task2.cancel() tasks = [task1, task2, task3] for coro in asyncio.as_completed(tasks): result = await coro print(result) time_end = time.time() print(f'运行耗时:{time_end-time_start}s') print('协程结束') if __name__ == '__main__': print(f'当前时间:{time.ctime()}') print() asyncio.run(main_wait_cancel()) asyncio.run(main_gather_cancel()) asyncio.run(main_as_completed())

    使用wait来等待任务完成,取消任务没有问题:

    if __name__ == '__main__': print(f'当前时间:{time.ctime()}') print() asyncio.run(main_wait_cancel())

    结果如下: 使用gather来取消任务,抛出异常:

    if __name__ == '__main__': print(f'当前时间:{time.ctime()}') print() asyncio.run(main_gather_cancel())

    结果如下: 使用as_completed来收集任务,这里没有取消任务,否则出现错误:

    if __name__ == '__main__': print(f'当前时间:{time.ctime()}') print() asyncio.run(main_as_completed())

    结果如下: 异步任务创建好了之后是可以取消的,取消之后的任务可以在wait中执行,但是在其他的收集函数中执行会引发异常,所以取消部分任务之后还要完成所有任务时要小心,避免抛出异常。

    喜欢python的朋友可以关注微信公众号“与C同行”:

    Processed: 0.009, SQL: 9