Python concurrent.futures.wait 怎样区分已完成和未完成任务
使用 Python 线程池或进程池时,Future 列表经常不是同时结束的。concurrent.futures.wait 的作用不是按完成顺序逐个产出结果,而是把任务集合切成两个集合:返回时已经完成或已取消的 done,以及仍在运行或尚未开始的 not_done。先看这两个集合,再决定读取结果、取消任务或继续等待,通常比对所有 Future 直接调用 result() 更容易控制。
官方地址:https://docs.python.org/3/library/concurrent.futures.html
done包含已完成和已取消的 Future;not_done包含返回时仍未完成的 Future。return_when决定何时返回,默认是ALL_COMPLETED;timeout只限制本次等待。- 读取结果要在
done集合上逐个调用result(),并单独捕获任务异常。
先分清 wait 返回的两个集合
wait(fs) 返回一个命名二元组,字段名就是 done 和 not_done。它们是集合而不是按提交顺序排列的列表,因此不要用集合顺序推断任务先后。已取消的 Future 也属于完成状态;只要在 wait 返回的那个时刻已经完成或取消,就会进入 done。
下面的例子故意让任务耗时不同,用 FIRST_COMPLETED 观察“本次等待结束时”的状态划分:
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED
import time
def load_part(name, delay):
# 用不同耗时模拟并发读取,返回值仍然由 Future 保存。
time.sleep(delay)
return f"{name}: ok"
with ThreadPoolExecutor(max_workers=3) as executor:
futures = {
executor.submit(load_part, "A", 0.1),
executor.submit(load_part, "B", 0.4),
executor.submit(load_part, "C", 0.8),
}
# FIRST_COMPLETED 表示任意一个 Future 完成或取消后就返回。
done, not_done = wait(futures, return_when=FIRST_COMPLETED)
print("已完成数量:", len(done))
print("未完成数量:", len(not_done))

这里的 done 可能只有 A,也可能包含多个已经同时完成的任务;not_done 里的 Future 可能处于运行中,也可能还排在执行队列里。想知道某个 Future 是否完成,可以调用它的 done(),但不应该把 not_done 直接当成“永远不会完成”的任务。
return_when 决定等待策略,timeout 决定等待上限
这两个参数解决的是不同问题:return_when 定义“什么事件发生后返回”,timeout 定义“最多等多久”。超时返回时,函数仍然会正常给出两个集合,不会因为有 Future 没完成就自动抛出超时异常。
| 配置 | 返回时机 | 适合场景 |
|---|---|---|
ALL_COMPLETED | 全部 Future 完成或取消 | 批量任务必须一起汇总 |
FIRST_COMPLETED | 任一 Future 完成或取消 | 先拿到一个可用结果 |
FIRST_EXCEPTION | 任一 Future 抛出异常;没有异常时等同于全部完成 | 首个失败就进入降级判断 |
例如,批量任务允许最多等待 0.5 秒,但希望首个异常尽快暴露,可以这样写:
from concurrent.futures import FIRST_EXCEPTION, ThreadPoolExecutor, wait
def run_job(job_id):
# 真实项目中可在这里调用外部服务,并让异常保留给主线程判断。
if job_id == "bad":
raise ValueError("任务参数无效")
return f"{job_id}: done"
with ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(run_job, job_id) for job_id in ("a", "bad", "c")]
# timeout 只限制 wait 的阻塞时间,不会替 Future 取消任务。
done, not_done = wait(futures, timeout=0.5, return_when=FIRST_EXCEPTION)
print("本次返回:", len(done), len(not_done))
如果 0.5 秒到了仍没有任务完成,done 可能为空,全部 Future 都在 not_done 中。不要用“返回了”推断“任务已经成功”,成功与否仍要读取 done 里的结果。
读取 done 结果时要把异常当成结果的一部分处理
wait 只负责状态集合,不会替你读取结果,也不会把任务异常提前抛出。对 done 中的 Future 调用 result() 时,任务内部抛出的异常会在这里重新抛出;已取消的 Future 则会抛出 CancelledError。因此应当逐个处理,而不是让一个异常中断整个汇总循环。
from concurrent.futures import CancelledError
success = []
failures = []
for future in done:
try:
# 这里不会再次等待,因为 wait 已经把 Future 放进 done。
success.append(future.result())
except CancelledError:
# 取消也是一种已完成状态,单独记录便于后续补偿。
failures.append("cancelled")
except Exception as exc:
# 只记录当前任务的错误,继续收集其它 Future 的结果。
failures.append(str(exc))
如果业务需要把任务和业务编号对应起来,可以在提交时建立 future_to_job 映射。不要依赖集合遍历顺序,也不要通过打印顺序判断哪个任务先结束。
not_done 怎么处理:继续等、取消,还是交给下一轮
超时后,not_done 是一个明确的决策入口。正在运行的 Future 通常不能被 cancel();尚未开始执行的 Future 才可能取消成功。因此可以记录未完成任务并在下一轮查询,而不能把 cancel() 的返回值假设为必然成功。

# 超时后只尝试取消仍未开始的任务,并保留取消结果。
cancelled = []
for future in not_done:
# running 的 Future 会返回 False,说明它仍需等待或由执行器收尾。
if future.cancel():
cancelled.append(future)
print("成功取消:", len(cancelled))
print("仍需跟踪:", len(not_done) - len(cancelled))
wait 和 as_completed 的边界也要分清:前者适合一次得到“已完成/未完成”的集合,后者适合按完成顺序迭代 Future。若需求是“任意完成一个就返回并保留剩余任务”,选 FIRST_COMPLETED;若需求是“每完成一个立即消费”,则应考虑 as_completed,不要把两个 API 混成同一种遍历。
常见问题
wait 的 done 集合是按完成顺序排列的吗?
不是。done 是集合,表达的是状态归属,不提供可依赖的完成顺序。需要顺序消费时使用 as_completed。
timeout 到期会自动取消 not_done 吗?
不会。timeout 只让 wait 返回,任务是否继续运行由执行器和任务本身决定;要取消必须显式调用 cancel(),并检查返回值。
FIRST_EXCEPTION 没有异常时什么时候返回?
如果没有 Future 抛出异常,它等同于 ALL_COMPLETED,会等所有 Future 完成或取消。
把 wait 看成“状态分组器”就不容易误用:先根据 return_when 和 timeout 得到当下状态,再逐个处理 done,最后对 not_done 做继续等待或取消决策。
Python zipfile.ZipFile.open 怎样按二进制流读取压缩成员
- 上一篇
- Python zipfile.ZipFile.open 怎样按二进制流读取压缩成员
- 下一篇
- Linux systemd-analyze dot 怎样导出 unit 依赖图
-
- 文章 · python教程 | 1小时前 |
- Python zipfile.ZipFile.open 怎样按二进制流读取压缩成员
- 340浏览 收藏
-
- 文章 · python教程 | 1小时前 | python · Python 进程间通信 multiprocessing shared_memory ShareableList
- Python ShareableList 固定长度共享列表的类型与容量边界
- 163浏览 收藏
-
- 文章 · python教程 | 2小时前 | 工程实践 · Python教程 · Python 对象生命周期 weakref weakref.finalize 清理回调
- Python weakref.finalize 怎样为对象注册清理回调
- 200浏览 收藏
-
- 文章 · python教程 | 2小时前 | python · Python TypeGuard 类型收窄 typing.TypeIs
- Python typing.TypeIs 怎样表达更精确的类型收窄
- 218浏览 收藏
-
- 文章 · python教程 | 2小时前 |
- Python dataclasses.field 怎样声明不可共享的默认值
- 250浏览 收藏
-
- 文章 · python教程 | 2小时前 | SQLite · Python教程 · Python sqlite3 sqlite3.Row row_factory 按列名读取
- Python sqlite3.Row 怎样按列名读取查询结果
- 197浏览 收藏
-
- 文章 · python教程 | 3小时前 | Python asyncio CancelledError 异步超时 asyncio.timeout TimeoutError
- Python asyncio.timeout 怎样把超时变成可捕获异常
- 419浏览 收藏
-
- 文章 · python教程 | 3小时前 | 消息队列 · python · 异步编程 · Python asyncio Queue.shutdown QueueShutDown
- Python asyncio.Queue.shutdown 怎样让生产者和消费者安全收尾
- 312浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 844次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 844次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 926次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 900次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 730次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- Go保证并发安全底层实现详解
- 2023-02-24 417浏览
-
- Go语言开发保证并发安全实例详解
- 2023-01-07 328浏览
-
- Golang 手写一个简单的并发任务 manager
- 2022-12-23 367浏览
-
- Go语言使用goroutine及通道实现并发详解
- 2023-01-02 221浏览

