Python multiprocessing中独立进程的mp.Value变量异常被篡改问题问询
问题分析与解决方案
这个问题的根源在Windows系统下multiprocessing的spawn启动机制,以及mp.Value的命名共享内存实现方式上,具体原因和解决办法如下:
为什么会出现“跨进程修改”的诡异现象?
在Windows中,multiprocessing默认使用spawn模式创建子进程——这种模式下,每个子进程会重新加载整个脚本,并通过pickle序列化传递参数。而mp.Value在Windows下是通过命名共享内存实现的:
- 如果你没有给
mp.Value指定唯一的name参数,系统会自动生成一个临时名称。 - 当你快速连续创建两个
mp.Value时(比如间隔0.03秒),系统可能会复用之前的共享内存名称,导致两个进程的mp.Value意外指向了同一块共享内存区域!
这就解释了你看到的现象:第二个Worker进程初始化mp.Value为-1时,直接覆盖了第一个进程已经改成1的共享内存值,触发了第一个进程的非法变更检测。
解决方案
1. 给每个mp.Value指定唯一名称
显式给每个共享变量设置独一无二的name参数,避免系统自动生成的名称冲突:
import multiprocessing as mp import time class Worker: def __init__(self, tag, service_state) -> None: self.tag = tag self.local_state = int(service_state.value) self.state = service_state self.run_work_loop() def run_work_loop(self) -> None: print(f"[{self.tag}] Running... {self.state.value} {self.local_state}") while True: if self.state.value != self.local_state: print(f"[{self.tag}] Illegal change. Shared state: {self.state.value} Local State: {self.local_state}") break elif self.state.value == -1: self.state.value = self.local_state = 1 print(f"[{self.tag}] Set Shared State: {self.state.value} Local State: {self.local_state}.") time.sleep(0.01) # 加sleep避免CPU跑满 if __name__ == "__main__": # 给每个Value指定唯一name mp.Process(target=Worker, args=("A", mp.Value('i', -1, name="worker_A"))).start() time.sleep(.03) mp.Process(target=Worker, args=("B", mp.Value('i', -1, name="worker_B"))).start()
2. 使用mp.Manager创建共享变量(更推荐)
mp.Manager会统一管理共享资源,自动处理命名和隔离问题,比直接用mp.Value更安全:
import multiprocessing as mp import time class Worker: def __init__(self, tag, service_state) -> None: self.tag = tag self.local_state = service_state.value self.state = service_state self.run_work_loop() def run_work_loop(self) -> None: print(f"[{self.tag}] Running... {self.state.value} {self.local_state}") while True: if self.state.value != self.local_state: print(f"[{self.tag}] Illegal change. Shared state: {self.state.value} Local State: {self.local_state}") break elif self.state.value == -1: self.state.value = self.local_state = 1 print(f"[{self.tag}] Set Shared State: {self.state.value} Local State: {self.local_state}.") time.sleep(0.01) if __name__ == "__main__": with mp.Manager() as manager: # 通过Manager创建共享Value val_a = manager.Value('i', -1) val_b = manager.Value('i', -1) mp.Process(target=Worker, args=("A", val_a)).start() time.sleep(.03) mp.Process(target=Worker, args=("B", val_b)).start()
3. 规范multiprocessing的使用方式
更符合Python多进程规范的做法是让Worker继承mp.Process,重写run方法,而不是在__init__里直接启动循环:
import multiprocessing as mp import time class Worker(mp.Process): def __init__(self, tag, service_state) -> None: super().__init__() self.tag = tag self.local_state = int(service_state.value) self.state = service_state def run(self) -> None: print(f"[{self.tag}] Running... {self.state.value} {self.local_state}") while True: if self.state.value != self.local_state: print(f"[{self.tag}] Illegal change. Shared state: {self.state.value} Local State: {self.local_state}") break elif self.state.value == -1: self.state.value = self.local_state = 1 print(f"[{self.tag}] Set Shared State: {self.state.value} Local State: {self.local_state}.") time.sleep(0.01) if __name__ == "__main__": with mp.Manager() as manager: val_a = manager.Value('i', -1) val_b = manager.Value('i', -1) worker_a = Worker("A", val_a) worker_a.start() time.sleep(.03) worker_b = Worker("B", val_b) worker_b.start()
额外提示
- 工作循环里一定要加
time.sleep(),否则进程会占用100% CPU,不仅影响系统性能,还可能导致一些难以排查的调度问题。 - Linux/macOS下使用
fork模式创建进程,不会出现这个问题(因为fork是复制父进程内存,mp.Value用匿名共享内存),但Windows下必须注意spawn模式的特性。
内容的提问来源于stack exchange,提问作者Alan
相关产品推荐
相关产品推荐

