Python concurrent.futures map 的 buffersize 怎样限制排队
我第一次把一批大文件交给 ThreadPoolExecutor.map() 时,误以为“线程数是 8,所以同时只会准备 8 个输入”。实际观察到的却是:任务函数还没来得及处理完,输入生成器已经被持续消费,内存随着待处理数据一起涨。后来把 buffersize 加上,才把“线程并发数”和“已经提交、但结果还没交付的任务数”分成了两个可以分别控制的旋钮。
官方文档:https://docs.python.org/3/library/concurrent.futures.html
buffersize 限制的是 map 已提交但结果尚未被调用方取走的任务数量;缓冲区满时,输入迭代器会暂停。它不是线程数,也不是结果队列的总容量。该参数从 Python 3.14 开始提供。
先分清 buffersize 到底限制什么
Executor.map(fn, iterable, buffersize=N) 的关键是“提交”和“交付”之间的窗口。调用方还没有从返回迭代器取走结果时,已提交任务会占用这个窗口;窗口满了,map() 不再继续向输入迭代器要新数据,直到至少有一个结果被取走。
这和 max_workers 的职责不同:max_workers 控制同时运行的 worker 数量,buffersize 控制调用方尚未消费结果时,允许任务继续向前排队多少。实际运行中,缓冲区里可能同时包含运行中任务和已经完成但尚未交付的任务。

我用一个慢任务看出排队窗口
下面的生成器每产生一个编号就打印一次。任务故意睡眠一小段时间,主线程也故意慢慢读取结果。把 buffersize 改成 2、4 或 None,可以直观看到输入生成器何时被背压。示例使用 Python 3.14 及更高版本。
import time
from concurrent.futures import ThreadPoolExecutor
def source():
# 用打印观察 map 何时继续消费输入;真实项目可替换为分页读取器。
for number in range(8):
print(f"produce {number}")
yield number
def work(number):
# 模拟一个有延迟的 I/O 任务,避免所有结果立刻完成。
time.sleep(0.2)
return number * number
with ThreadPoolExecutor(max_workers=2) as executor:
# buffersize=2 让未被主循环取走的提交任务保持在一个小窗口内。
results = executor.map(work, source(), buffersize=2)
for result in results:
print("consume", result)
# 模拟下游写入或网络发送变慢的情况。
time.sleep(0.4)
这里不要把打印顺序当成严格的调度证明;线程什么时候完成取决于机器负载。但有一个稳定的语义:当主循环不取结果且窗口已经满时,source() 会暂时停止产生新输入。buffersize=None 则恢复“先收集可迭代对象,再尽量提交”的旧行为,长输入更容易把内存压力推到调用方看不见的地方。
buffersize、max_workers 和 chunksize 不是一回事
我在第一次调参时把这三个参数一起改,结果很难判断哪个参数真正起作用。更稳妥的做法是先固定 worker 数量,再单独改变提交窗口:
| 参数 | 主要控制对象 | 常见误解 |
|---|---|---|
max_workers | 同时执行函数的线程或进程数量 | 以为它限制了输入生成器的消费量 |
buffersize | 已提交但尚未交付给调用方的结果窗口 | 以为它等于 worker 数量 |
chunksize | ProcessPoolExecutor 向进程池分块提交输入的近似大小 | 以为线程池也会按块执行 |
官方文档明确说明,chunksize 只会显著影响 ProcessPoolExecutor 的分块提交;对 ThreadPoolExecutor 和 InterpreterPoolExecutor 没有效果。buffersize 则是 map 层的结果交付窗口,二者可以同时设置,但不要用一个参数代替另一个参数。

按序消费会让一个慢结果挡住后面的结果
map() 返回的迭代器按输入顺序产出结果。即使后面的任务已经完成,只要前面的任务还没完成,调用方仍然会在前面等待。此时降低 buffersize 能控制内存和输入压力,却不会把 map() 变成“谁先完成就先返回”。
如果业务真正需要完成即处理,应改用 submit() 配合 as_completed(),并自行维护任务与输入的对应关系。这个选择的代价是代码需要处理异常、取消和结果排序,不能只替换一个参数。
from concurrent.futures import ThreadPoolExecutor, as_completed
def work(number):
# 真实任务中应在函数内部做好资源释放和必要的超时控制。
return number * number
with ThreadPoolExecutor(max_workers=4) as executor:
futures = {
executor.submit(work, number): number
for number in range(8)
}
for future in as_completed(futures):
number = futures[future]
try:
print(number, future.result())
except Exception as exc:
# 异常在取 Future 结果时显式处理,避免静默丢失失败项。
print("failed", number, repr(exc))
异常、超时和边界值要提前定规则
如果任务函数抛出异常,map() 迭代器会在调用方取到对应结果时重新抛出。已经提交的其他任务不因为这一项异常就自动全部取消,所以需要在业务层决定是否停止继续消费、关闭 executor 或记录失败输入。
timeout 的计时起点是调用 Executor.map() 的时刻,而不是每次调用 next() 的时刻;当调用方等待超过限制,会得到 TimeoutError。这和 worker 函数内部的网络超时不是同一层控制。
- Python 版本:在 Python 3.13 及更早版本,
Executor.map()没有buffersize参数;需要兼容旧版本时应使用手动submit()窗口或版本分支。 - 空输入:不会产生任务,
buffersize没有可观察效果。 - 小于等于零:不要把 0 当作“完全不排队”;按当前文档语义应传正整数,非法值应在应用启动或配置读取阶段拒绝。
- 多个 iterable:输入按位置配对,最短 iterable 耗尽后停止;窗口限制仍针对已提交但未交付的结果。
什么时候值得使用 buffersize
对我来说,最值得用它的场景是“输入很长、每项占用资源、下游消费速度不稳定”:例如分页读取后并发处理、批量调用外部服务、把结果持续写入文件或数据库。它能把输入端的消费速度绑定到结果端的推进速度,降低无界预提交带来的内存和连接压力。
如果输入本来就只有几十项,或者任务函数几乎瞬间完成,设置它通常不会带来明显收益。此时优先把任务超时、错误处理、幂等和 worker 数量定清楚。对 CPU 密集型任务,还要先判断线程池是否合适,不能用 buffersize 掩盖执行模型选错的问题。
我的实际判断清单
- 先确认运行环境是否至少是 Python 3.14。
- 把
max_workers按外部服务容量或 CPU 资源单独估算。 - 如果输入是生成器且不能一次性装进内存,再设置一个小的正整数
buffersize。 - 确认业务接受按输入顺序交付;需要完成即处理时改用
submit()与as_completed()。 - 为函数异常、全局超时、取消和重复执行定义处理策略,并记录输入标识。
一句话总结:max_workers 管“正在干活的工人”,buffersize 管“还没交到你手里的已提交工作”。把两者分开后,map() 的排队行为才真正可控。
相关问题
buffersize=1 会退化成串行吗?
不会。worker 仍由 max_workers 决定,多个任务可以并行运行;只是调用方不取结果时,允许继续保持的未交付窗口更小。
能用 buffersize 限制外部 API 的 QPS 吗?
不能直接等价。它限制的是提交和交付之间的任务窗口,不是时间维度的速率;QPS 仍需要限速器、并发信号量或服务端配额控制。
为什么用了 buffersize 内存仍然会上升?
窗口只约束 map 的提交侧。worker 自身缓存、任务参数、结果对象、下游写入队列以及外部库的连接池仍可能占用内存,需要分别测量和设置上限。
Python itertools.islice 怎样截取迭代器而不提前展开
- 上一篇
- Python itertools.islice 怎样截取迭代器而不提前展开
- 下一篇
- Python logging.basicConfig(force=True) 怎样重置根日志配置
-
- 文章 · python教程 | 39分钟前 |
- Python ipaddress.collapse_addresses 怎样合并连续网段
- 334浏览 收藏
-
- 文章 · python教程 | 41分钟前 |
- Python logging.basicConfig(force=True) 怎样重置根日志配置
- 434浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python itertools.islice 怎样截取迭代器而不提前展开
- 383浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python itertools.batched 怎样处理末尾不足一组的数据
- 141浏览 收藏
-
- 文章 · python教程 | 1小时前 | 标准库 · 调试 · python · Python 装饰器 functools.wraps functools.update_wrapper __wrapped__ inspect.unwrap
- Python functools.update_wrapper 怎样保留装饰器元数据
- 174浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python functools.total_ordering 怎样补齐自定义类的比较方法
- 282浏览 收藏
-
- 文章 · python教程 | 1小时前 | python · Python 继承 链式调用 类型标注 typing.Self
- Python typing.Self 怎样标注链式调用的返回类型
- 118浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python pickletools.dis 怎样查看 pickle 字节码结构
- 148浏览 收藏
-
- 文章 · python教程 | 2小时前 |
- Python types.MappingProxyType 怎样创建只读映射视图
- 203浏览 收藏
-
- 文章 · python教程 | 3小时前 |
- Python sqlite3 URI 文件名怎样打开只读数据库
- 218浏览 收藏
-
- 文章 · python教程 | 1天前 |
- Python tabnanny 怎样定位缩进中混用制表符的问题
- 153浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 821次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 814次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 904次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 876次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 697次使用
-
- Golang WorkerPool线程池并发模式示例详解
- 2022-12-28 170浏览
-
- SingleFlight模式的Go并发编程学习
- 2023-01-01 285浏览
-
- Go并发编程之sync.Once使用实例详解
- 2022-12-27 484浏览
-
- Go并发编程之goroutine使用正确方法
- 2022-12-31 241浏览
-
- go格式“占位符”输入输出 类似python的input
- 2023-01-19 346浏览

