Python 多进程 Queue 传输结果的关闭方式
Python 的 multiprocessing.Queue 适合在多个进程之间传递结果,但它不是一个“调用 close() 就立刻结束”的普通列表。对象会先被序列化,生产进程还可能通过后台 feeder thread 把缓冲区里的数据刷入管道。正确的收尾顺序通常是:主进程先持续 get(),确认生产端都发出结束标记后再 join() 子进程,最后关闭主进程自己的队列句柄。
官方地址:https://docs.python.org/3/library/multiprocessing.html
下面用一个“多个 worker 返回结果”的小场景说明这套顺序。重点不是把所有进程 API 罗列一遍,而是解决结果已经放进 Queue 后,程序退出时为什么还会卡住的问题。
Queue 关闭前要先理解 feeder thread
multiprocessing.Queue.put() 并不等于调用进程已经把完整对象同步写入管道。Queue 会对对象做序列化,并在需要时启动后台 feeder thread,将进程内的缓冲内容继续送往管道。消费进程通过 get() 读取的是已经进入队列传输通道的数据。

因此,close() 的含义是释放当前进程持有的 Queue 内部资源,并且这个进程不能再对它调用 put() 或 get()。它不是向其它进程广播“所有数据都已被读取”的信号,也不替代进程间的结束协议。
先约定结果和结束标记
多个 worker 时,主进程不能只依赖 empty() 判断任务是否结束。队列可能正在接收数据,empty() 也不适合作为可靠的跨进程完成条件。更稳妥的方式是让每个 worker 在结果之后放入一个 None sentinel,主进程收到与 worker 数量相同的 sentinel 后,才认为生产端全部结束。
这个约定把“业务结果”和“生产者结束”区分开:普通字典是业务数据,None 只负责结束计数。若业务本身允许返回 None,就改用一个明确的字典标记或专用对象。
一套可复用的安全收尾代码
下面的例子使用 spawn 上下文,显式展示跨平台进程入口。worker 完成最后一次 put() 后关闭自己持有的队列句柄,并等待自己的 feeder thread 刷完;主进程则一边消费,一边等待所有 sentinel。
import multiprocessing as mp
def worker(result_queue, worker_id):
try:
# 结果对象会被序列化后交给 Queue 的传输缓冲区。
result_queue.put({"worker": worker_id, "value": worker_id * 10})
# sentinel 只表示这个生产者不再提交新的结果。
result_queue.put(None)
finally:
# 当前 worker 不再使用自己的 Queue 句柄,先释放内部资源。
result_queue.close()
# 等待本进程 feeder thread 把已入队数据刷入管道。
result_queue.join_thread()
if __name__ == "__main__":
# spawn 要求进程创建代码放在保护块中。
context = mp.get_context("spawn")
result_queue = context.Queue()
processes = [
context.Process(target=worker, args=(result_queue, worker_id))
for worker_id in range(3)
]
for process in processes:
# 先启动生产者,再由主进程持续取结果。
process.start()
results = []
finished_workers = 0
while finished_workers join_thread 顺序清理。
result_queue.close()
result_queue.join_thread()
print(sorted(results, key=lambda item: item["worker"]))
这段代码的关键点是主进程没有在 process.join() 前停止读取 Queue。对于单个小结果,错误顺序可能暂时看不出问题;当结果较大或数量较多时,子进程可能还在等待 feeder thread 刷空缓冲区,主进程却先等待子进程结束,于是形成互相等待。

close、join_thread 和 process.join 分别负责什么
可以把三个方法分成三种对象来理解:
| 方法 | 作用对象 | 正确使用时机 |
|---|---|---|
Queue.close() | 当前进程的 Queue 句柄 | 当前进程不再 put/get 时调用,之后不要继续使用该 Queue |
Queue.join_thread() | 当前进程的 feeder thread | 先 close,再等待缓冲数据刷入管道 |
Process.join() | 子进程本身 | 主进程已经消费完所需结果或结束标记后调用 |
生产端和消费端各自持有 Queue 的进程内句柄,所以 worker 的 close() 不会让主进程的 get() 失效。主进程必须等自己的消费工作完成后再关闭自己的句柄。若只有一个生产者,也可以用一个 sentinel;如果任务数量本身固定,也可以按固定结果数读取,但仍要保证所有生产者的进程回收顺序不会堵住未消费的数据。
最容易踩中的两种关闭错误
错误一:先 join 子进程,再读取 Queue
# 可能造成阻塞:子进程的缓冲数据还没有被消费。 process.join() item = result_queue.get()
当子进程向 Queue 放入的数据较大时,子进程退出前需要 feeder thread 把缓冲内容送入管道。主进程先 join(),就没有机会继续 get() 腾出传输空间。修复方式是把 get() 放在前面,或者使用独立的协调协议,确保消费者不会停读。
错误二:close 后继续 put 或 get
# close 表示当前进程不再使用这个 Queue。
result_queue.close()
# 这里继续 put/get 属于错误的生命周期使用。
result_queue.put({"late": True})
如果业务还需要发送数据,说明关闭时机太早,应把 close() 放到最后一次 put() 之后。关闭后是否还有其它进程在工作,也不改变当前进程不能继续使用该句柄的规则。
cancel_join_thread 什么时候可以用
cancel_join_thread() 会阻止进程退出时自动等待 Queue 的后台线程。它适合明确接受“进程可以马上退出,未刷完的数据可能丢失”的场景,例如放弃一批无关紧要的临时统计。它不是加速版的 join_thread(),也不是常规生产代码的默认清理方式。
# 只有明确允许丢弃尚未刷出的临时数据时才这样做。 result_queue.cancel_join_thread() result_queue.close()
正式结果、任务状态和需要审计的消息都不应依赖这个方法保证退出速度。若目标是可靠传输,应让消费者读完、生产端关闭并等待 feeder thread,再回收进程。
排查 Queue 关闭问题的顺序
- 确认每个 worker 是否都在最后一次结果后发送了结束标记。
- 确认主进程是否持续
get(),而不是先对生产者调用join()。 - 确认 worker 的
close()和join_thread()位于最后一次put()之后。 - 确认主进程收到所有 sentinel 后才回收进程,并最后清理自己的 Queue。
- 只有在明确允许数据丢失时,才考虑
cancel_join_thread()。
记住一句话:Queue 的“关闭”是某个进程释放自己的使用权,进程的“join”是等待子进程结束,二者都不能代替消费协议。把结束标记、消费循环和清理顺序写清楚,多进程传输结果就不容易在退出阶段变成一个隐蔽的死锁。
相关问题
Queue.close() 会通知其它进程停止吗?
不会。它只关闭调用者当前持有的 Queue 资源;跨进程停止需要 sentinel、事件或其它明确协议。
为什么 Queue.put() 成功了,进程还是退出很慢?
因为 put 后的数据可能仍在 feeder thread 的缓冲区中,进程退出时需要等待刷入管道。主进程应继续消费,生产端再按 close 和 join_thread 收尾。
Python statistics.quantiles 计算 P95 时的 n 参数与方法选择
- 上一篇
- Python statistics.quantiles 计算 P95 时的 n 参数与方法选择
- 下一篇
- Python 正则输入规模与超时防护的边界
-
- 文章 · python教程 | 48分钟前 |
- Python wheel 构建元数据和依赖声明
- 182浏览 收藏
-
- 文章 · python教程 | 56分钟前 |
- Python 正则输入规模与超时防护的边界
- 487浏览 收藏
-
- 文章 · python教程 | 1小时前 | python · 数据分析 · Python 分位数 P95 p99 statistics.quantiles
- Python statistics.quantiles 计算 P95 时的 n 参数与方法选择
- 225浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python logging lastResort 怎样处理没有配置 handler 的日志
- 289浏览 收藏
-
- 文章 · python教程 | 2天前 | 并发编程 · Python教程 · Python logging 后台线程 logging.handlers.QueueListener 日志关闭
- Python logging.handlers.QueueListener 怎样优雅停止后台日志线程
- 143浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python tomllib 怎样读取二进制 TOML
- 114浏览 收藏
-
- 文章 · python教程 | 2天前 | 文件处理 · Python教程 · Python 目录复制 shutil.copytree dirs_exist_ok 文件覆盖
- Python shutil.copytree 怎样合并已有目标目录
- 165浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python pathlib 复制文件时怎样控制符号链接
- 478浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python inspect.signature 怎样读取装饰器保留的参数
- 298浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 500次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 558次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 586次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 537次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 349次使用
-
- Python sqlite3 Connection serialize 怎么导出数据库快照:备份窗口、内存占用与恢复校验
- 2026-08-26 501浏览
-
- Python监控网页状态:requests异常处理实战
- 2026-05-29 501浏览
-
- TensorFlow模型部署为API的TF Serving方法
- 2026-05-26 501浏览
-
- Python字符串编码转换:encode与decode详解
- 2026-05-16 501浏览
-
- TensorFlow裁剪无用算子方法详解
- 2026-05-15 501浏览
