You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在asyncio.run()外部等待asyncio futures的运行机制疑问

关于asyncio.run()外部等待Future的疑问

我在某代码库中遇到如下模式,这打破了我对asyncio工作机制的认知。我们有一个函数会await一部分futures集合,返回另一部分耗时更长的futures集合,随后在asyncio.run()外部等待这部分返回的futures。我不确定这些后续的futures在asyncio.run()外无事件循环的情况下如何获取CPU时间,是否在调用f.result()时它们才有机会运行/完成?

async def foo():
    futures_a = ...
    futures_b = ...

    await asyncio.gather(*futures_a)

    return futures_b

async def main():
    tasks = set()

    tasks.add(asyncio.create_task(foo()))
    tasks.add(asyncio.create_task(foo()))
    tasks.add(asyncio.create_task(foo()))

    return await asyncio.gather(*tasks)


pending_futures = asyncio.run(main())

for f in pending_futures:
    f.result()

解答

首先明确:这些返回的futures_b不是在调用f.result()时才开始运行,它们的执行逻辑取决于自身类型:

1. 如果futures_b是concurrent.futures.Future(比如通过loop.run_in_executor()创建的线程/进程任务)

这类Future的执行完全不依赖asyncio事件循环,任务会在独立的线程或进程池中运行。当foo()创建它们时,后台任务就已经启动了——哪怕await futures_a在等待,这些线程/进程任务也会并行执行。

当asyncio.run()结束、事件循环关闭后,这些后台任务仍会继续运行。调用f.result()只是阻塞当前主线程,等待后台任务完成并返回结果,此时不需要asyncio事件循环参与。

2. 如果futures_b是asyncio.Task(通过asyncio.create_task()创建的异步任务)

这种情况代码逻辑是有问题的:

  • asyncio.Task的执行完全依赖绑定的事件循环。当asyncio.run()结束时,它会关闭内部创建的事件循环,未完成的Task会被暂停(甚至可能被标记为取消)。
  • 此时调用f.result()会一直阻塞,因为没有事件循环驱动Task继续执行,最终要么抛出CancelledError,要么永远无法得到结果。

所以你看到的这个模式能正常工作,前提是futures_b是不依赖asyncio事件循环的线程/进程池Future。你之前的误解在于混淆了asyncio Task和普通Future的执行依赖——前者必须靠事件循环调度,后者靠线程/进程池独立运行。


内容的提问来源于stack exchange,提问作者Milad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 19:22:17