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

已取消任务调用result()抛出InvalidStateError而非CancelledError问题咨询

问题:asyncio任务取消后调用result()抛出InvalidStateError的原因及规范实现

我构建了如下结构,旨在调用一系列异步操作,在任一操作完成或超时到期时立即返回。但运行时发现,读取已取消任务的结果时抛出的是InvalidStateError而非预期的CancelledError。

import asyncio

async def async_operation():
        while True:
                await asyncio.sleep(1)

async def main():
    task1 = None
    task2 = None
    try:
        task1 = asyncio.create_task(async_operation())
        task2 = asyncio.create_task(async_operation())

        await asyncio.wait([task1, task2], timeout=2, return_when=asyncio.FIRST_COMPLETED)
    finally:
            if task1 and not task1.done():
                print("Cancelling task1!") # this is printed!
                task1.cancel()
            if task2 and not task2.done():
                task2.cancel()
    try:
            # This throws:
            #   asyncio.exceptions.InvalidStateError: Result is not set.
            res = task1.result()
            # I would expect one of the following two cases to be true:
            #
            #   1. the task completes and thus the result is set or
            #   2. it is cancelled in the finally block and thus calling .result()
            #      should raise CancelledError and not InvalidStateError
            print(f"Got result: ${res}")
    except asyncio.CancelledError:
            print(f"Caught CancelledError!")

asyncio.run(main())

原因解释

task.cancel()仅会给任务添加一个取消标记,并不会立即终止任务执行。任务只有在下次遇到await这类可等待对象时,才会触发CancelledError;如果任务内部没有捕获这个异常,异常会向上传播,任务才会进入已取消完成的状态。

在你的代码中,调用task1.cancel()后,没有等待任务处理取消流程就直接调用result()——此时任务仍处于pending状态(虽然被标记为取消,但还没执行到await asyncio.sleep(1)这个节点触发异常)。这种情况下调用result(),因为任务既没有正常完成(无结果),也没有因取消而完成(未触发异常),所以会抛出InvalidStateError。

规范实现建议

要让任务真正完成取消流程,需要在调用cancel()后等待任务结束,同时正确处理任务状态:

改进后的代码示例

import asyncio

async def async_operation():
    while True:
        await asyncio.sleep(1)

async def main():
    task1 = None
    task2 = None
    try:
        task1 = asyncio.create_task(async_operation())
        task2 = asyncio.create_task(async_operation())

        await asyncio.wait([task1, task2], timeout=2, return_when=asyncio.FIRST_COMPLETED)
    finally:
        # 收集需要取消的任务
        pending_tasks = []
        if task1 and not task1.done():
            print("Cancelling task1!")
            task1.cancel()
            pending_tasks.append(task1)
        if task2 and not task2.done():
            task2.cancel()
            pending_tasks.append(task2)
        
        # 等待任务完成取消流程,return_exceptions=True避免异常传播
        if pending_tasks:
            await asyncio.gather(*pending_tasks, return_exceptions=True)
    
    # 处理任务结果
    if task1.done():
        try:
            res = task1.result()
            print(f"Got result: {res}")
        except asyncio.CancelledError:
            print(f"Caught CancelledError for task1!")
        except Exception as e:
            print(f"Task1 raised exception: {e}")

asyncio.run(main())

关键要点

  • 调用cancel()后,必须await任务,让它完成取消流程,进入done状态。
  • 使用return_exceptions=True调用asyncio.gather(),可以避免取消任务的异常向上传播导致程序崩溃。
  • 调用result()前,先通过task.done()检查任务状态,确保任务已完成(无论正常还是异常)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 10:03:22