Python 32:一个子任务失败后其他任务怎么办
三个任务已经进入 running:一个准备计算时抛异常,另外两个还在等永远不会设置的 Event。若只等待失败任务,两个等待中的兄弟任务可能留在后台。业务库里看到 failed,并不能据此推断同批其他任务都停止了。
31 的 asyncio 入口保留任务状态合同。本篇增加 run_batch:每个 Task 都在 asyncio.TaskGroup 中创建;正常退出组时收集词频结果,某个任务首次抛出非 CancelledError 异常时,任务组取消其余成员、等待它们退出,再向调用方抛出异常组。批次不允许同一任务 ID 重复提交;在安排任何工作前先拒绝重复项。
失败以后先等谁退出
测试给三项工作各设一扇事件门,等三边都报告 running 才只放行故障任务。该任务触发可控 RuntimeError,经业务入口转为 TaskExecutionError 并记录 failed;另外两扇门始终没有打开。任务组取消还在等门的两个兄弟任务;run_async 接收 CancelledError 时标记 cancelled 并重新抛出。外层 worker 的 finally 在每个子任务结束时设置观察事件。调用方捕获到只有一个 TaskExecutionError 的 ExceptionGroup 后,三项 finally 观察事件都已设置,两个兄弟任务都没有结果。
这里的 cleaned 只证明本章 finally 路径运行并退出了作用域,不证明数据库提交、外部请求或进程强杀后的资源都已完成回收。协作取消必须运行到可取消的等待点;30 的进程池也不能以强杀替代这个 Python 清理信号。不要在子任务里吞掉 CancelledError 后装作任务正常停止,TaskGroup 与超时作用域都依赖取消传播。
超时与取消请求的两个时刻
同一工作单元等一扇不打开的事件门,asyncio.timeout_at(loop.time() + 0.1) 到期后取消当前作用域;退出上下文时调用方捕获 TimeoutError。观测到任务开始、门未打开、finally 事件已设置、最终状态为 cancelled。0.1 秒是本机测试的超时阈值,不是精确停止耗时,也不是任何平台的性能基准。
另一个实验先对正在等待的任务调用旧的 cancel(store, id):返回真时状态只是 cancel_requested,清理事件仍未设置。随后显式取消包含任务组的父 Task,等待它抛出 CancelledError 后,状态才是 cancelled、退出事件已记录。请求被接受与工作实际停止之间有时间差;若计算全程没有挂起点,还不能凭 cancel() 返回真承诺马上中断。完成状态也不能倒写成取消。
1 | |
本次补跑输出 {"task-0001":"failed","task-0002":"cancelled","task-0003":"cancelled"},并确认 all_cleaned=true、两个兄弟任务的门都没有放行;随后仍覆盖超时后的 cancelled、取消请求从 cancel_requested 到 cancelled,以及正常批次的 succeeded 与原词频结果。原始输出见 examples/python-language/evidence/completion/core/32-three-task-after-experiment.log,对应测试见 32-three-task-after-unittest.log。这是内存任务及 Event 的可控测试;超时环境下复杂外部副作用是否完成、进程退出和事务恢复在本章均未验收,不能从观察事件为真推出恰好一次执行。
预测题
三个任务都已进入 running,故障任务的门打开,另外两个任务一直等待。失败传出 TaskGroup 后,三个任务状态各是什么?异常的外层类型是什么?
答案:故障任务 failed,两个等待者收到取消后都是 cancelled;外层为 ExceptionGroup,其中的业务异常是 TaskExecutionError。三项 finally 记录在离开任务组前都已经设置;不会把等待者未打开的门解释成它完成了计算。
变更题
在运行中只调用 cancel(store, id),不取消 Task,也不打开事件门;或者在子任务里捕获 CancelledError 却直接返回成功。这两种改动能保证在截止时间后批次全部停止吗?
答案:都不能。前者只把业务状态改成 cancel_requested,等待中的协程并未得到取消;后者会破坏取消传播,可能让结构化并发及超时的含义失真。应保留取消传播、让任务在受控等待点收到取消,并在退出任务组后检查实际状态与 finally。若工作进入不可中断的外部副作用,需另记录副作用完成证据。
系列导航只列已验收文章。




