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

Python 3.11中TaskGroup遇任务异常冻结是否为已知Bug?

Python 3.11 TaskGroup 任务取消冻结问题:已知情况与规避方案

问题确认

这是Python 3.11中asyncio.TaskGroup与asyncio.wait_for结合使用时的已知功能性Bug。由于Python 3.11目前处于维护阶段(仅接收安全相关修复,不处理功能性Bug),官方不会针对此问题发布修复补丁。

问题根源

当TaskGroup中的某个任务抛出异常时,TaskGroup会尝试取消剩余任务,但Python 3.11中wait_for内部的取消处理逻辑存在缺陷:被取消时,wait_for无法正确唤醒正在等待Event.wait()的任务,导致任务永久阻塞,最终整个事件循环挂起。

规避方案

以下几种调整方式可解决该问题,在Python 3.11中实现预期的任务取消逻辑:

方案1:用asyncio.wait替代wait_for

asyncio.wait的取消逻辑在3.11中不受此Bug影响,可替换原代码中的wait_for调用:

import asyncio

ev = asyncio.Event()

async def t1():
    try:
        while True:
            try:
                print("Waiting")
                # 用wait替代wait_for实现超时等待
                done, pending = await asyncio.wait([ev.wait()], timeout=99)
                if ev.is_set():
                    print("Done waiting")
                    ev.clear()
                else:
                    print("Timeout")
                    raise TimeoutError
            except TimeoutError:
                print("Timeout")
                raise
    except asyncio.CancelledError:
        print("Cancelled -  as expected")
        raise

async def t2():
    ev.set()
    raise RuntimeError()

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            tg.create_task(t1())
            tg.create_task(t2())
    except* RuntimeError:
        print("RuntimeError -  as expected")

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

方案2:提前检查任务取消状态

在进入wait_for等待前,主动检查当前任务是否已被取消,提前触发CancelledError:

import asyncio

ev = asyncio.Event()

async def t1():
    try:
        while True:
            # 让出控制权并检查取消状态
            await asyncio.sleep(0)
            if asyncio.current_task().cancelled():
                raise asyncio.CancelledError()
            
            try:
                print("Waiting")
                await asyncio.wait_for(ev.wait(), 99)
                print("Done waiting")
                ev.clear()
            except TimeoutError:
                print("Timeout")
                raise
    except asyncio.CancelledError:
        print("Cancelled -  as expected")
        raise

async def t2():
    ev.set()
    raise RuntimeError()

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            tg.create_task(t1())
            tg.create_task(t2())
    except* RuntimeError:
        print("RuntimeError -  as expected")

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

方案3:自定义超时等待函数

实现一个不受该Bug影响的自定义超时等待逻辑:

import asyncio

ev = asyncio.Event()

async def wait_with_timeout(fut, timeout):
    """自定义超时等待函数"""
    timeout_task = asyncio.create_task(asyncio.sleep(timeout))
    done, pending = await asyncio.wait(
        [fut, timeout_task],
        return_when=asyncio.FIRST_COMPLETED
    )
    if timeout_task in done:
        fut.cancel()
        raise TimeoutError
    else:
        timeout_task.cancel()
        return await fut

async def t1():
    try:
        while True:
            try:
                print("Waiting")
                await wait_with_timeout(ev.wait(), 99)
                print("Done waiting")
                ev.clear()
            except TimeoutError:
                print("Timeout")
                raise
    except asyncio.CancelledError:
        print("Cancelled -  as expected")
        raise

async def t2():
    ev.set()
    raise RuntimeError()

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            tg.create_task(t1())
            tg.create_task(t2())
    except* RuntimeError:
        print("RuntimeError -  as expected")

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

验证效果

以上三种方案在Python 3.11中运行后,均可得到与Python 3.12一致的预期输出:

Waiting
Done waiting
Waiting
Cancelled -  as expected
RuntimeError -  as expected

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:43:13