科技 · 實用指南
Python 警告「resource_tracker: leaked semaphore objects」?隱藏前先找到失效持有者
這條結束警告是清理症狀,不是根因。先保留第一個例外,列出每個程序建立的 semaphore,再證明 close、join 與 unlink 的正常順序。

解释器關閉時,resource tracker 仍保存已登记名稱就会发出警告。它不会直接告诉你哪个库建立了資源,也不證明最後一行就是根因;被信号杀死的子程序、初始化失败、未關閉的程序池、未排空的队列或重复 unlink 都可能留下同樣警告。
从首个异常和持有者台账開始。記錄 Python 版本與启动方式,每次只保留一種資源构造器,證明每个子程序的退出及父程序的 close、shutdown、join、unlink 路径;只有生命周期已被证實時,才讨论是否過滤警告。
先保存首个故障,不要只看最後警告
保存完整 stderr 和退出状態,找到 resource-tracker 行之前最早的 traceback、信号、initializer 失败、超時、取消或强制终止。最後警告可能只是次生清理。
記錄 Python 构建、操作系统、启动方式、父子 PID、库版本,以及程序是正常返回、抛错、取消还是被杀。不要先過滤 UserWarning。
列出构造器并分配資源持有者
檢查應用與依赖边界中的 multiprocessing.Pool、Process、Queue、Lock、Semaphore、ProcessPoolExecutor、SharedMemory、joblib 或数據加载 worker。記錄构造位置、所属程序以及负责關閉或 unlink 的作用域。
需要 spawn-safe import 的平台應把程序建立放进 main guard,但不要把它当通用修复;import 時建立資源、initializer 失败或库内部程序池仍有不同原因。
先證明正常退出,再测试崩溃分支
优先使用上下文管理器;否则先關閉程序池使其停止接收任务,再 join;關閉 executor;生产者结束後關閉队列,并按文档 join feeder thread;SharedMemory 由约定的单一持有者 unlink 一次。
基线執行不要 terminate、kill、os._exit、突然停容器或让测试超時。Python 文档明确指出强制终止可能留下共享資源、锁、管道和队列。
区分真實遗留對象與 tracker 记账
把程序缩减到一个构造器和一个子程序,并記錄建立、子程序退出、close、join、unlink 與解释器關閉顺序。若警告消失,再逐层加入库,直到所有權发生变化。
僅在 Linux 上,/dev/shm 可作為 POSIX 命名對象的辅助证據;它不是安全刪除清单,其他平台也不同。无法绑定到已停止程序和正式清理持有者的名稱不能刪除。
测试受支援版本與竞態分支
若生命周期正确,在当前受支援的补丁版本重现最小脚本。CPython issue 顯示,initializer 失败、重入清理和重复 unlink 竞態在特定版本或测试中都可能产生相似警告。
不要為了安静而把私有 unregister 调用粘进生产。先确认是重复清理、完全没有清理,还是可在特定版本重現的問题。
用无警告、无遗留對象的執行收尾
收尾标准是:每个子程序退出明确,父程序關閉并 join 所有資源,SharedMemory 只有一个 unlink 持有者,解释器關閉不再产生新警告。还要在原 service manager 或 CI 取消路径重复测试。
只有完成上述證明後,才可為已知无害的第三方警告设置窄過滤,并記錄版本、最小重現、上游 issue 與移除条件。
