在 multiprocessing 中,子进程无法共享父进程的全局变量内存空间,每个进程拥有独立副本,因此对全局列表的修改仅限于当前进程内,主进程中的原始变量保持不变。
在 multiprocessing 中,子进程无法共享父进程的全局变量内存空间,每个进程拥有独立副本,因此对全局列表的修改仅限于当前进程内,主进程中的原始变量保持不变。
当你使用 multiprocessing.Pool 时,Python 会通过 fork(Unix/Linux/macOS)或 spawn(Windows)方式创建全新独立进程,而非线程。这些子进程在启动时会复制父进程的内存状态(包括全局变量 foo = []),但此后彼此内存完全隔离——子进程中对 foo.append(1) 的操作仅修改其本地副本,主进程中的 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()
results = pool.map(square, range(1, 5))
pool.close()
pool.join()
print(f"[Main] id(foo)={id(foo)}, foo={foo}") # 输出: [Main] id(...), foo=[]
运行结果中,各子进程会输出不同 id(foo) 和非空 foo,而主进程始终显示空列表——这正是进程内存隔离的直接体现。
✅ 正确做法:使用 multiprocessing 提供的进程安全通信机制替代全局变量,例如:
- Manager().list():支持跨进程共享的代理列表(性能稍低,但最易用)
- multiprocessing.Queue 或 Pipe:适用于生产者-消费者模式
- multiprocessing.Array / Value:适用于简单类型(如数字计数)
示例(推荐 Manager.list):
import multiprocessing as mp
def worker(n, shared_list):
shared_list.append(n * n) # 安全写入共享列表
return n * n
if __name__ == "__main__":
with mp.Manager() as manager:
shared_foo = manager.list() # 创建可跨进程共享的列表
with mp.Pool() as pool:
# 传入共享对象到每个worker
pool.starmap(worker, [(i, shared_foo) for i in range(1, 5)])
print("Shared result:", list(shared_foo)) # ✅ 输出: [1, 4, 9, 16]
⚠️ 注意事项:
- 所有跨进程共享对象必须在 if __name__ == "__main__": 下创建(尤其 Windows 上防止递归启动);
- Manager 对象存在序列化开销,高频写入场景建议改用 Queue 或预分配 Array;
- 切勿依赖装饰器中隐式修改全局变量的方式实现进程间状态同步——这是常见误区,本质违反 multiprocessing 设计模型。
总结:multiprocessing ≠ multithreading。线程共享内存,进程不共享。理解这一根本差异,是写出健壮并行程序的第一步。











