如何精确控制 asyncio 中并发运行的多个任务( 七 )


为什么要先将协程包装成任务我们说协程在传给 wait 的时候会自动包装成任务,那为什么我们还要手动包装呢?
import asyncioasync def delay(seconds):await asyncio.sleep(seconds)return f"我睡了 {seconds} 秒"async def main():tasks = [asyncio.create_task(delay(seconds))for seconds in range(1, 6)]done, pending = await asyncio.wait(tasks, timeout=3.1)print(all(map(lambda t: t in tasks, done)))print(all(map(lambda t: t in tasks, pending)))loop = asyncio.get_event_loop()loop.run_until_complete(main())"""TrueTrue"""如果 wait 函数接收的就是任务,那么 wait 函数就不会再包装了,所以 done 和 pending 里面的任务和 tasks 里面的任务是相同的 。基于这个条件,我们后续可以做一些比较之类的 。
比如有很多 Web 请求任务,但如果当未完成的任务是 task1、task2、task3 , 那么就取消掉,于是可以这么做 。
for t in pending:if t in (task1, task2, task3):t.cancel()如果返回的 done 和 pending 里的任务,是在 wait 函数中自动创建的,那么我们就无法进行任何比较来查看 pending 集合中的特定任务 。
小结1)asyncio.gather 函数允许同时运行多个任务,并等待它们完成 。一旦传递给它的所有任务全部完成,这个函数就会返回 。由于 gather 会拿到里面每个任务的返回值 , 所以它要求每个任务都是成功的,如果有任务执行出错(没有返回值) , 那么获取返回值的时候就会将异常抛出来,然后向上传递给 await asyncio.gather 。
为此,可以将 return_exceptions 设置为 True,这将返回成功完成的可等待对象的结果,以及产生的异常(异常会作为一个普通的属性返回,和返回值是等价的) 。
2)可使用 as_completed 函数在可等待对象列表完成时处理它们的结果,它会返回一个可以循环遍历的生成器 。一旦某个协程或任务完成,就能访问结果并处理它 。
3)如果希望同时运行多个任务 , 并且还希望能了解哪些任务已经完成,哪些任务在运行,则可以使用 wait 。这个函数还允许在返回结果时进行更多控制 , 返回时,我们会得到一组已经完成的任务和一组仍在运行的任务 。
然后可以取消任何想要取消的任务,或执行其他任何需要执行的任务 。并且 wait 里面的任务出现异常,也不会影响其它任务,异常会作为任务的一个属性,只是在我们没有处理的时候会给出警告 。至于具体的处理方式 , 我们直接通过 exception 方法判断是否发生了异常即可,没有异常返回 result(),有异常返回 exception() 。




推荐阅读