Python多进程传递Process对象触发Pickling报错的解决方案
问题描述
在同一工作目录下创建了两个Python文件:
- 业务模块
script.py:包含一个持续循环运行、无法中途停止或暂停的函数,代码如下:
# script.py def run(): i = 0 while True: print('\r', i, '\r', end='') i += 1
- 自定义调试模块
debugger.py:用于监听目录内文件更新,检测到变更时重启业务进程,代码如下:
# debugger.py import os import wait from multiprocessing import Process, Queue def capture_change(processesQueue): prev = os.path.getsize('wait.py') while True: curr = os.path.getsize('wait.py') if curr != prev: print('Changes Detected!!') process = processesQueue.get() process.kill() process.start() processesQueue.put(process) prev = curr if __name__ == "__main__": processesQueue = Queue() p1 = Process(target= wait.run) processesQueue.put(p1) p2 = Process(target= capture_change, args= (processesQueue, )) p1.start() p2.start()
运行上述代码时抛出如下错误:
raise TypeError( TypeError: Pickling an AuthenticationString object is disallowed for security reasons
尝试检索多种多进程间数据共享方案后,发现均不支持直接在进程间传递Process实例,需要可行的解决思路。
错误根因
报错的核心原因是设计逻辑存在问题:multiprocessing.Queue跨进程传递对象时,需要对对象做pickle序列化,而Process实例内部包含的AuthenticationString字段出于安全考虑是禁止被序列化的,因此跨进程传递Process实例本身就是不被Python支持的操作,不需要寻找能支持传递Process实例的跨进程通信方案,调整架构即可解决。
解决思路
核心原则:Process实例的生命周期管控(启动、杀掉、重启)必须在创建它的父进程中完成,不要跨进程传递Process对象。
具体实现逻辑:
- 把文件变更检测的逻辑单独放在子进程中运行,检测到文件变更时,只需要通过Queue向主进程传递简单的可序列化信号(比如字符串
RELOAD、布尔值True这类基础类型),不要传Process对象。 - 主进程负责持有业务进程的实例,收到重启信号后,自主完成旧业务进程终止、业务模块重载、新业务进程启动的全流程。
修正后的debugger.py参考代码如下:
import os import time import script from multiprocessing import Process, Queue def capture_change(signal_queue): watch_file = "script.py" prev_mtime = os.path.getmtime(watch_file) while True: curr_mtime = os.path.getmtime(watch_file) if curr_mtime != prev_mtime: print("Changes Detected!!") signal_queue.put("RELOAD") prev_mtime = curr_mtime time.sleep(0.5) # 增加轮询间隔,避免CPU空转 if __name__ == "__main__": signal_queue = Queue() # 启动初始业务进程 biz_process = Process(target=script.run) biz_process.start() # 启动文件监听进程 watcher_process = Process(target=capture_change, args=(signal_queue,)) watcher_process.daemon = True watcher_process.start() # 主进程循环处理信号,管控业务进程生命周期 while True: sig = signal_queue.get() if sig == "RELOAD": # 终止旧业务进程 if biz_process.is_alive(): biz_process.kill() biz_process.join() # 重载业务模块,加载最新代码 import importlib importlib.reload(script) # 启动新的业务进程 biz_process = Process(target=script.run) biz_process.start()
额外注意事项:
- 原代码存在笔误:业务模块文件名为
script.py,但导入时写的是wait,运行前需要修正。 - 文件变更检测建议用修改时间
os.path.getmtime判断,不要仅判断文件大小,否则修改后文件大小不变的场景会漏检。 - 文件轮询逻辑需要增加适当的sleep间隔,避免死循环空转占满CPU资源。
内容的提问来源于stack exchange,提问作者Aman Ahmed Siddiqui
相关产品推荐
相关产品推荐

