
多进程程序中,每个子进程拥有独立内存空间,对全局变量的修改不会同步回主进程,导致主进程看到的仍是初始值。
多进程程序中,每个子进程拥有独立内存空间,对全局变量的修改不会同步回主进程,导致主进程看到的仍是初始值。
在 Python 的 `multiprocessing` 模块中,子进程并非线程,而是通过 `fork`(Unix/Linux/macOS)或 `spawn`(Windows)方式创建的**全新进程**,拥有完全独立的内存地址空间。这意味着:主进程中定义的全局变量(如 `foo = []`)在每个子进程中都会被复制一份——是**副本,而非引用**。因此,装饰器中对 `foo.append(1)` 的调用,仅修改了当前子进程内的 `foo` 副本;主进程中的 `foo` 自始至终未被触碰,最终打印时自然为空列表。以下代码清晰展示了该现象的本质:
import multiprocessing as mp
import functools
foo = []
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
foo.append(1)
print(f"[PID {mp.current_process().pid}] id(foo)={id(foo)}, foo={foo}")
return f(*args, **kwargs)
return wrapper
@decorator
def square(n):
return n * n
if __name__ == "__main__":
pool = mp.Pool(processes=2)
results = pool.map(square, [1, 2, 3, 4])
pool.close()
pool.join()
print(f"[Main] id(foo)={id(foo)}, foo={foo}") # 输出: [Main] id(foo)=..., foo=[]
运行结果类似:
[PID 12345] id(foo)=140234567890123, foo=[1] [PID 12346] id(foo)=140234567890456, foo=[1] [PID 12345] id(foo)=140234567890123, foo=[1, 1] [PID 12346] id(foo)=140234567890456, foo=[1, 1] [Main] id(foo)=140234567890789, foo=[]
可见:各子进程内 foo 地址不同、内容独立增长;而主进程的 foo 地址唯一且始终为空。
✅ 正确做法:使用 multiprocessing 提供的进程安全共享机制,例如:
-
Manager.list()(推荐用于小规模、动态结构):
from multiprocessing import Pool, Manager def worker_with_manager(shared_list, n): shared_list.append(1) # 线程/进程安全 return n * n if __name__ == "__main__": with Manager() as manager: shared_foo = manager.list() # 创建可共享列表 with Pool() as pool: results = pool.starmap( worker_with_manager, [(shared_foo, i) for i in range(1, 5)] ) print("Shared result:", list(shared_foo)) # [1, 1, 1, 1] multiprocessing.Queue 或 Pipe(适合流式收集、解耦生产/消费);
multiprocessing.Value / Array(适用于固定类型、高性能场景,如计数器、数值数组)。
⚠️ 注意事项:
- 切勿依赖全局变量跨进程通信,这是根本性设计误区;
- Windows/macOS 使用 spawn 启动方式时,模块级变量初始化行为更严格,务必确保 if __name__ == "__main__": 保护;
- Manager 对象存在序列化开销,高频写入场景建议改用 Queue 或预分配 Array。
总结:理解“进程隔离”是驾驭 multiprocessing 的前提。放弃对全局变量的幻想,转而使用显式、受控的 IPC 工具,才能构建健壮的并行程序。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











