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

如何让Python asyncio后台任务的异常立即抛出?

如何让需定期重启的后台asyncio任务异常立即触发并终止其他任务?

问题场景

我有多个持续运行的asyncio任务,其中bar任务需要定期重启,但它抛出的异常要等到下一次重启尝试时才会被捕获。如果重启间隔很长,异常会长期无法被察觉,同时其他任务(如foo)也无法及时停止。

原示例中:

  • foo:持续运行无异常
  • bar:迭代5次后抛出异常
  • on_interval:每3秒重启bar

原代码的问题是bar抛出异常后,由于没有被立即await,异常会被挂起,直到下一次on_interval执行重启逻辑时才会被发现。

解决方案

通过任务完成回调+共享错误事件的方式,实现异常的即时捕获,并同步终止所有相关任务:

import asyncio

async def foo(error_event: asyncio.Event):
    while not error_event.is_set():
        print("foo")
        await asyncio.sleep(1)
    print("foo stopping due to error")

async def bar(error_event: asyncio.Event):
    i = 0
    while not error_event.is_set():
        i += 1
        if i > 4:
            raise ValueError("bar exceeded iteration limit")

        print("bar", i)
        await asyncio.sleep(1)

def handle_bar_exception(task: asyncio.Task, error_event: asyncio.Event):
    try:
        task.result()  # 主动获取任务结果,触发异常
    except Exception as e:
        print(f"bar failed immediately with error: {e}")
        error_event.set()  # 设置错误事件,通知所有任务停止

async def on_interval(n: int, error_event: asyncio.Event):
    task = None
    while not error_event.is_set():
        # 启动bar任务并绑定异常回调
        print("Starting bar")
        task = asyncio.create_task(bar(error_event))
        task.add_done_callback(lambda t: handle_bar_exception(t, error_event))
        
        await asyncio.sleep(n)
        
        if not error_event.is_set():  # 无异常时才执行定期重启
            print("Canceling bar for scheduled restart")
            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                pass
            print("Restarting bar")
    print("on_interval stopping due to error")

async def main():
    error_event = asyncio.Event()
    await asyncio.gather(
        foo(error_event),
        on_interval(3, error_event),
        return_exceptions=True  # 避免单个任务异常导致gather直接终止
    )

if __name__ == "__main__":
    asyncio.run(main())

核心改进点

  1. 共享错误事件:用asyncio.Event作为全局信号,所有任务循环都检查该事件状态,一旦异常触发就停止运行。
  2. 即时异常捕获:给bar任务绑定add_done_callback,任务异常退出时立即执行回调,通过task.result()主动抛出并处理异常,同时设置错误事件。
  3. 优雅终止所有任务:foo和on_interval都会监听错误事件,事件触发后自动退出循环,实现所有任务的同步终止。
  4. 避免全局变量:用参数传递事件和任务,替代原代码中的全局变量,代码更清晰可维护。

效果验证

运行修改后的代码,bar在输出bar 4后会立即抛出异常,回调函数会打印错误信息,同时foo会在当前循环结束后停止,不会等到下一次重启间隔。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 21:13:14