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

Python asyncio中Future取消后回调未触发及死循环问题咨询

问题分析与解决方案

首先,你的代码里有两个核心问题导致了当前的异常现象:

1. 无限阻塞的check_stats协程

你的check_stats是异步函数,但里面的while循环没有任何await操作——这会让这个协程完全占据事件循环,导致其他任务(包括future的回调函数done_callback)根本没有机会被执行。事件循环被这个死循环卡住,自然无法处理future.cancel()触发的回调,也永远不会执行loop.stop()。

2. 回调执行时机与事件循环调度

当你调用future.cancel()时,虽然future的状态会立刻变为cancelled和done,但add_done_callback注册的回调函数并不是同步执行的——它需要等待事件循环调度执行。而被死循环卡住的事件循环根本无法处理这个回调任务。


修改后的代码

我们需要给check_stats的循环加上await来让出事件循环,同时调整细节确保逻辑符合预期:

import asyncio

_future_set = asyncio.Future()

def done_callback(f):
    if f.exception():
        _future_set.set_exception(f.exception())  # 注意要调用exception()方法,而非直接引用
    elif f.cancelled():
        if not _future_set.done():
            _future_set.cancel()
    else:
        _future_set.set_result(None)

async def check_stats(future):
    while not future.done():  # 检查future是否完成(包含取消状态)
        print("not done")
        await asyncio.sleep(0.1)  # 让出事件循环,让其他任务有执行机会
    loop.stop()

def set(future):
    if not _future_set.done():
        future.add_done_callback(done_callback)

loop = asyncio.new_event_loop()
future = loop.create_future()
asyncio.ensure_future(check_stats(future), loop=loop)
set(future)
future.cancel()
loop.run_forever()
loop.close()  # 事件循环结束后关闭,避免资源泄漏
print(_future_set.cancelled())  # 现在会输出True

关键修改说明

  • 给check_stats添加await:await asyncio.sleep(0.1)会让协程暂停并让出事件循环,这样事件循环就能处理done_callback的执行请求,同时也能在future完成(被取消)时退出循环并调用loop.stop()。
  • 修复f.exception()的调用:原代码里写的是f.exception(仅引用方法),应该改为f.exception()(调用方法获取异常对象),否则会抛出类型错误。
  • 在done_callback里增加状态检查:确保只有当_future_set未完成时才执行取消操作,避免不必要的错误。
  • 添加loop.close():事件循环结束后关闭是Python异步编程的良好实践,避免资源泄漏。

这样修改后,代码就能按照你的预期运行:future.cancel()触发done_callback,_future_set被取消,最终print(_future_set.cancelled())会输出True,事件循环也能正常终止。

内容的提问来源于stack exchange,提问作者Idan Haim Shalom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:21:18