1、核心概念与背景
1.1 三种启动模式对比
Python 3.8+ 支持三种创建子进程的方式:
| 模式 | 平台 | 机制 | 速度 | 继承性 | 安全性 |
|---|---|---|---|---|---|
| fork | Unix/macOS | 复制父进程内存空间(OS fork) | ⚡ 极快 | 继承所有资源 | ⚠️ 线程不安全 |
| spawn | Unix/macOS/Windows | 从头启动全新的 Python 解释器 | 🐢 较慢 | 不继承资源 | ✅ 安全 |
| forkserver | Unix | 启动一个 server 进程,由其 fork | ⚡ 快 | 部分继承 | ✅ 较安全 |
1.2 为什么 Windows 默认用 spawn?
- Windows 没有
fork()系统调用 - 若使用 fork,线程锁会被复制到新进程,可能引发死锁
- spawn 虽然慢,但干净、安全、可预测
2、spawn 模式的底层原理
Python 的 spawn 模式是多进程启动中最"干净"但也最重的方案。其核心是子进程不复制父进程内存,而是启动一个全新的 Python 解释器**,再通过序列化传递执行目标。
2.1 核心原理
spawn 与 fork 的本质区别:
| 特性 | spawn |
fork |
|---|---|---|
| 启动方式 | 新建 Python 解释器进程 | 系统调用 fork() 复制地址空间 |
| 内存继承 | ❌ 不继承父进程内存 | ✅ 继承(写时复制) |
| 启动速度 | 慢(需重新初始化) | 快 |
| 线程安全 | ✅ 安全(无锁复制问题) | ❌ 不安全(仅复制主线程) |
| 全局变量 | ❌ 子进程不可见 | ✅ 子进程可见 |
| 参数传递 | 必须通过 pickle 序列化 |
直接共享内存 |
2.2 底层执行流程
#mermaid-svg-a8YHei1feukd6Xd4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a8YHei1feukd6Xd4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a8YHei1feukd6Xd4 .error-icon{fill:#552222;}#mermaid-svg-a8YHei1feukd6Xd4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a8YHei1feukd6Xd4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a8YHei1feukd6Xd4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a8YHei1feukd6Xd4 .marker.cross{stroke:#333333;}#mermaid-svg-a8YHei1feukd6Xd4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a8YHei1feukd6Xd4 p{margin:0;}#mermaid-svg-a8YHei1feukd6Xd4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a8YHei1feukd6Xd4 .cluster-label text{fill:#333;}#mermaid-svg-a8YHei1feukd6Xd4 .cluster-label span{color:#333;}#mermaid-svg-a8YHei1feukd6Xd4 .cluster-label span p{background-color:transparent;}#mermaid-svg-a8YHei1feukd6Xd4 .label text,#mermaid-svg-a8YHei1feukd6Xd4 span{fill:#333;color:#333;}#mermaid-svg-a8YHei1feukd6Xd4 .node rect,#mermaid-svg-a8YHei1feukd6Xd4 .node circle,#mermaid-svg-a8YHei1feukd6Xd4 .node ellipse,#mermaid-svg-a8YHei1feukd6Xd4 .node polygon,#mermaid-svg-a8YHei1feukd6Xd4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a8YHei1feukd6Xd4 .rough-node .label text,#mermaid-svg-a8YHei1feukd6Xd4 .node .label text,#mermaid-svg-a8YHei1feukd6Xd4 .image-shape .label,#mermaid-svg-a8YHei1feukd6Xd4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-a8YHei1feukd6Xd4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a8YHei1feukd6Xd4 .rough-node .label,#mermaid-svg-a8YHei1feukd6Xd4 .node .label,#mermaid-svg-a8YHei1feukd6Xd4 .image-shape .label,#mermaid-svg-a8YHei1feukd6Xd4 .icon-shape .label{text-align:center;}#mermaid-svg-a8YHei1feukd6Xd4 .node.clickable{cursor:pointer;}#mermaid-svg-a8YHei1feukd6Xd4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a8YHei1feukd6Xd4 .arrowheadPath{fill:#333333;}#mermaid-svg-a8YHei1feukd6Xd4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a8YHei1feukd6Xd4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a8YHei1feukd6Xd4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a8YHei1feukd6Xd4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a8YHei1feukd6Xd4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a8YHei1feukd6Xd4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a8YHei1feukd6Xd4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a8YHei1feukd6Xd4 .cluster text{fill:#333;}#mermaid-svg-a8YHei1feukd6Xd4 .cluster span{color:#333;}#mermaid-svg-a8YHei1feukd6Xd4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a8YHei1feukd6Xd4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a8YHei1feukd6Xd4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-a8YHei1feukd6Xd4 .icon-shape,#mermaid-svg-a8YHei1feukd6Xd4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a8YHei1feukd6Xd4 .icon-shape p,#mermaid-svg-a8YHei1feukd6Xd4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a8YHei1feukd6Xd4 .icon-shape .label rect,#mermaid-svg-a8YHei1feukd6Xd4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a8YHei1feukd6Xd4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a8YHei1feukd6Xd4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a8YHei1feukd6Xd4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 父进程
import multiprocessing
创建 Process(target=func, args=(x,))
调用 p.start()
启动模式判断
start_method='spawn'
操作系统 exec* 启动
全新的 python 解释器
子进程: 重新导入 main 模块
子进程: 从 sys.argv 读取标记
子进程: 反序列化 target / args
(通过管道/共享内存接收 pickle 数据)
子进程: 执行 func(x)
子进程退出
父进程继续执行
等待 join() 或异步运行
2.3 内存与资源边界
#mermaid-svg-za06OvCa2RvA2e8Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-za06OvCa2RvA2e8Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-za06OvCa2RvA2e8Y .error-icon{fill:#552222;}#mermaid-svg-za06OvCa2RvA2e8Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-za06OvCa2RvA2e8Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-za06OvCa2RvA2e8Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-za06OvCa2RvA2e8Y .marker.cross{stroke:#333333;}#mermaid-svg-za06OvCa2RvA2e8Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-za06OvCa2RvA2e8Y p{margin:0;}#mermaid-svg-za06OvCa2RvA2e8Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-za06OvCa2RvA2e8Y .cluster-label text{fill:#333;}#mermaid-svg-za06OvCa2RvA2e8Y .cluster-label span{color:#333;}#mermaid-svg-za06OvCa2RvA2e8Y .cluster-label span p{background-color:transparent;}#mermaid-svg-za06OvCa2RvA2e8Y .label text,#mermaid-svg-za06OvCa2RvA2e8Y span{fill:#333;color:#333;}#mermaid-svg-za06OvCa2RvA2e8Y .node rect,#mermaid-svg-za06OvCa2RvA2e8Y .node circle,#mermaid-svg-za06OvCa2RvA2e8Y .node ellipse,#mermaid-svg-za06OvCa2RvA2e8Y .node polygon,#mermaid-svg-za06OvCa2RvA2e8Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-za06OvCa2RvA2e8Y .rough-node .label text,#mermaid-svg-za06OvCa2RvA2e8Y .node .label text,#mermaid-svg-za06OvCa2RvA2e8Y .image-shape .label,#mermaid-svg-za06OvCa2RvA2e8Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-za06OvCa2RvA2e8Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-za06OvCa2RvA2e8Y .rough-node .label,#mermaid-svg-za06OvCa2RvA2e8Y .node .label,#mermaid-svg-za06OvCa2RvA2e8Y .image-shape .label,#mermaid-svg-za06OvCa2RvA2e8Y .icon-shape .label{text-align:center;}#mermaid-svg-za06OvCa2RvA2e8Y .node.clickable{cursor:pointer;}#mermaid-svg-za06OvCa2RvA2e8Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-za06OvCa2RvA2e8Y .arrowheadPath{fill:#333333;}#mermaid-svg-za06OvCa2RvA2e8Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-za06OvCa2RvA2e8Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-za06OvCa2RvA2e8Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-za06OvCa2RvA2e8Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-za06OvCa2RvA2e8Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-za06OvCa2RvA2e8Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-za06OvCa2RvA2e8Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-za06OvCa2RvA2e8Y .cluster text{fill:#333;}#mermaid-svg-za06OvCa2RvA2e8Y .cluster span{color:#333;}#mermaid-svg-za06OvCa2RvA2e8Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-za06OvCa2RvA2e8Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-za06OvCa2RvA2e8Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-za06OvCa2RvA2e8Y .icon-shape,#mermaid-svg-za06OvCa2RvA2e8Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-za06OvCa2RvA2e8Y .icon-shape p,#mermaid-svg-za06OvCa2RvA2e8Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-za06OvCa2RvA2e8Y .icon-shape .label rect,#mermaid-svg-za06OvCa2RvA2e8Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-za06OvCa2RvA2e8Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-za06OvCa2RvA2e8Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-za06OvCa2RvA2e8Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 子进程地址空间(spawn 后)
exec 新解释器
不复制内存
管道/队列传输
pickle 序列化
父进程地址空间
全局变量 g_var=100
已导入模块 numpy, pandas
Thread-1 (后台线程)
Process 对象
target=worker, args=(data,)
main 重新执行
仅继承文件描述符
0,1,2 (stdin/stdout/stderr)
通过 unpickle 重建 args
data' = pickle.loads(data)
执行 worker(data')
2.4 关键代码层面的行为
当使用 spawn 时,子进程会重新加载启动模块,这意味着:
python
import multiprocessing as mp
def worker(name):
print(f"Hello {name}")
if __name__ == '__main__':
mp.set_start_method('spawn')
p = mp.Process(target=worker, args=('Alice',))
p.start() # 触发 spawn 流程
p.join()
子进程实际执行的伪代码逻辑:
python
# 子进程内部(C 层实现,Python 不可见)
import sys
sys.path = [父进程 sys.path...] # 继承环境
# 重新导入 __main__,但通过隐藏标记跳过 if __name__=='__main__' 之后的代码
# 从管道读取 pickled (worker, ('Alice',))
func, args = unpickle_from_pipe()
func(*args) # 真正调用目标函数
2.5 为什么 spawn 更安全?
#mermaid-svg-9iJxlULE5ZOVLBvi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9iJxlULE5ZOVLBvi .error-icon{fill:#552222;}#mermaid-svg-9iJxlULE5ZOVLBvi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9iJxlULE5ZOVLBvi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9iJxlULE5ZOVLBvi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9iJxlULE5ZOVLBvi .marker.cross{stroke:#333333;}#mermaid-svg-9iJxlULE5ZOVLBvi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9iJxlULE5ZOVLBvi p{margin:0;}#mermaid-svg-9iJxlULE5ZOVLBvi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster-label text{fill:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster-label span{color:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster-label span p{background-color:transparent;}#mermaid-svg-9iJxlULE5ZOVLBvi .label text,#mermaid-svg-9iJxlULE5ZOVLBvi span{fill:#333;color:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi .node rect,#mermaid-svg-9iJxlULE5ZOVLBvi .node circle,#mermaid-svg-9iJxlULE5ZOVLBvi .node ellipse,#mermaid-svg-9iJxlULE5ZOVLBvi .node polygon,#mermaid-svg-9iJxlULE5ZOVLBvi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9iJxlULE5ZOVLBvi .rough-node .label text,#mermaid-svg-9iJxlULE5ZOVLBvi .node .label text,#mermaid-svg-9iJxlULE5ZOVLBvi .image-shape .label,#mermaid-svg-9iJxlULE5ZOVLBvi .icon-shape .label{text-anchor:middle;}#mermaid-svg-9iJxlULE5ZOVLBvi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9iJxlULE5ZOVLBvi .rough-node .label,#mermaid-svg-9iJxlULE5ZOVLBvi .node .label,#mermaid-svg-9iJxlULE5ZOVLBvi .image-shape .label,#mermaid-svg-9iJxlULE5ZOVLBvi .icon-shape .label{text-align:center;}#mermaid-svg-9iJxlULE5ZOVLBvi .node.clickable{cursor:pointer;}#mermaid-svg-9iJxlULE5ZOVLBvi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9iJxlULE5ZOVLBvi .arrowheadPath{fill:#333333;}#mermaid-svg-9iJxlULE5ZOVLBvi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9iJxlULE5ZOVLBvi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9iJxlULE5ZOVLBvi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9iJxlULE5ZOVLBvi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9iJxlULE5ZOVLBvi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9iJxlULE5ZOVLBvi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster text{fill:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi .cluster span{color:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9iJxlULE5ZOVLBvi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9iJxlULE5ZOVLBvi rect.text{fill:none;stroke-width:0;}#mermaid-svg-9iJxlULE5ZOVLBvi .icon-shape,#mermaid-svg-9iJxlULE5ZOVLBvi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9iJxlULE5ZOVLBvi .icon-shape p,#mermaid-svg-9iJxlULE5ZOVLBvi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9iJxlULE5ZOVLBvi .icon-shape .label rect,#mermaid-svg-9iJxlULE5ZOVLBvi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9iJxlULE5ZOVLBvi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9iJxlULE5ZOVLBvi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9iJxlULE5ZOVLBvi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} spawn 的安全保证
父进程: 多线程 + 全局锁
启动全新解释器
子进程无父进程线程
无锁继承问题
安全执行
fork 的风险场景
父进程: 多线程 + 全局锁
fork() 仅复制主线程
子进程持有锁
但无解锁线程
死锁!
macOS 和 Windows 默认使用 spawn ,正是因为它们对 fork() 的支持有限或存在线程安全问题。
2.6 使用注意事项
-
必须加
if __name__ == '__main__':否则子进程重新导入模块时会无限递归创建新进程。
-
所有参数必须可 pickle
spawn通过pickle序列化args和kwargs,不可序列化的对象(如文件句柄、未绑定方法)会报错。 -
全局状态不会继承
模块级全局变量、连接池等需要在子进程内重新初始化。
-
启动开销大
频繁创建销毁进程时,优先使用进程池
Pool或fork。
2.7 了解spawn 模式下通过管道传递
在 spawn 模式下,父进程和子进程是完全独立的内存空间,所以参数需要通过操作系统提供的 IPC(进程间通信)机制传递。Python 的 multiprocessing 在 spawn 模式下使用的是 os.pipe() 或类似的管道机制,把 target 函数和 args 通过 pickle 序列化后写入管道,子进程启动后从管道读出并反序列化。
形象化比喻:
- 管道像一根水管
- pickle 序列化像把物品打包成标准的集装箱
- 子进程启动时像快递收件
我需要画一个生动的图和比喻。
想象 spawn 启动子进程,就像爸爸要派一个机器人去隔壁房间执行任务------但这个机器人出厂时是空白的,你必须把所有"指令"和"工具"从门缝底下塞过去。
形象比喻:门缝下的快递包裹
#mermaid-svg-njHplWD2lxdxoSuW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-njHplWD2lxdxoSuW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-njHplWD2lxdxoSuW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-njHplWD2lxdxoSuW .error-icon{fill:#552222;}#mermaid-svg-njHplWD2lxdxoSuW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-njHplWD2lxdxoSuW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-njHplWD2lxdxoSuW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-njHplWD2lxdxoSuW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-njHplWD2lxdxoSuW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-njHplWD2lxdxoSuW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-njHplWD2lxdxoSuW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-njHplWD2lxdxoSuW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-njHplWD2lxdxoSuW .marker.cross{stroke:#333333;}#mermaid-svg-njHplWD2lxdxoSuW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-njHplWD2lxdxoSuW p{margin:0;}#mermaid-svg-njHplWD2lxdxoSuW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-njHplWD2lxdxoSuW .cluster-label text{fill:#333;}#mermaid-svg-njHplWD2lxdxoSuW .cluster-label span{color:#333;}#mermaid-svg-njHplWD2lxdxoSuW .cluster-label span p{background-color:transparent;}#mermaid-svg-njHplWD2lxdxoSuW .label text,#mermaid-svg-njHplWD2lxdxoSuW span{fill:#333;color:#333;}#mermaid-svg-njHplWD2lxdxoSuW .node rect,#mermaid-svg-njHplWD2lxdxoSuW .node circle,#mermaid-svg-njHplWD2lxdxoSuW .node ellipse,#mermaid-svg-njHplWD2lxdxoSuW .node polygon,#mermaid-svg-njHplWD2lxdxoSuW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-njHplWD2lxdxoSuW .rough-node .label text,#mermaid-svg-njHplWD2lxdxoSuW .node .label text,#mermaid-svg-njHplWD2lxdxoSuW .image-shape .label,#mermaid-svg-njHplWD2lxdxoSuW .icon-shape .label{text-anchor:middle;}#mermaid-svg-njHplWD2lxdxoSuW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-njHplWD2lxdxoSuW .rough-node .label,#mermaid-svg-njHplWD2lxdxoSuW .node .label,#mermaid-svg-njHplWD2lxdxoSuW .image-shape .label,#mermaid-svg-njHplWD2lxdxoSuW .icon-shape .label{text-align:center;}#mermaid-svg-njHplWD2lxdxoSuW .node.clickable{cursor:pointer;}#mermaid-svg-njHplWD2lxdxoSuW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-njHplWD2lxdxoSuW .arrowheadPath{fill:#333333;}#mermaid-svg-njHplWD2lxdxoSuW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-njHplWD2lxdxoSuW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-njHplWD2lxdxoSuW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-njHplWD2lxdxoSuW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-njHplWD2lxdxoSuW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-njHplWD2lxdxoSuW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-njHplWD2lxdxoSuW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-njHplWD2lxdxoSuW .cluster text{fill:#333;}#mermaid-svg-njHplWD2lxdxoSuW .cluster span{color:#333;}#mermaid-svg-njHplWD2lxdxoSuW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-njHplWD2lxdxoSuW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-njHplWD2lxdxoSuW rect.text{fill:none;stroke-width:0;}#mermaid-svg-njHplWD2lxdxoSuW .icon-shape,#mermaid-svg-njHplWD2lxdxoSuW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-njHplWD2lxdxoSuW .icon-shape p,#mermaid-svg-njHplWD2lxdxoSuW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-njHplWD2lxdxoSuW .icon-shape .label rect,#mermaid-svg-njHplWD2lxdxoSuW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-njHplWD2lxdxoSuW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-njHplWD2lxdxoSuW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-njHplWD2lxdxoSuW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 子进程房间
全新的空房间
父进程房间
pickle.dumps()
打包成字节流
pickle.dumps()
打包成字节流
字节流通过管道传输
🤖 目标函数: worker
📦 参数: data=1,2,3
🚪 管道 Pipe
os.pipe()
空白 Python 解释器
刚刚被'生'出来
📬 从门缝接包裹
拆包裹 → 拿出函数和参数
开始执行 worker(data)
关键点 :两个房间(进程)完全不共享任何家具(内存)。唯一的连接是一根细管子 (Pipe),只能通过这根管子塞字节流,不能直接把 Python 对象"扔"过去。
2.8 为什么必须用 pickle "打包"?
因为管道传输的是原始字节 (bytes),不是 Python 对象:
| 你在 Python 里看到的 | 管道里实际传输的 |
|---|---|
def worker(name): ... |
b'\x80\x04\x95...' (二进制字节码) |
data = {"key": [1, 2, 3]} |
b'\x80\x04\x95\x1c\x00...' |
#mermaid-svg-d0n6rUKAKd6x25nx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d0n6rUKAKd6x25nx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d0n6rUKAKd6x25nx .error-icon{fill:#552222;}#mermaid-svg-d0n6rUKAKd6x25nx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d0n6rUKAKd6x25nx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d0n6rUKAKd6x25nx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d0n6rUKAKd6x25nx .marker.cross{stroke:#333333;}#mermaid-svg-d0n6rUKAKd6x25nx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d0n6rUKAKd6x25nx p{margin:0;}#mermaid-svg-d0n6rUKAKd6x25nx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-d0n6rUKAKd6x25nx .cluster-label text{fill:#333;}#mermaid-svg-d0n6rUKAKd6x25nx .cluster-label span{color:#333;}#mermaid-svg-d0n6rUKAKd6x25nx .cluster-label span p{background-color:transparent;}#mermaid-svg-d0n6rUKAKd6x25nx .label text,#mermaid-svg-d0n6rUKAKd6x25nx span{fill:#333;color:#333;}#mermaid-svg-d0n6rUKAKd6x25nx .node rect,#mermaid-svg-d0n6rUKAKd6x25nx .node circle,#mermaid-svg-d0n6rUKAKd6x25nx .node ellipse,#mermaid-svg-d0n6rUKAKd6x25nx .node polygon,#mermaid-svg-d0n6rUKAKd6x25nx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d0n6rUKAKd6x25nx .rough-node .label text,#mermaid-svg-d0n6rUKAKd6x25nx .node .label text,#mermaid-svg-d0n6rUKAKd6x25nx .image-shape .label,#mermaid-svg-d0n6rUKAKd6x25nx .icon-shape .label{text-anchor:middle;}#mermaid-svg-d0n6rUKAKd6x25nx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d0n6rUKAKd6x25nx .rough-node .label,#mermaid-svg-d0n6rUKAKd6x25nx .node .label,#mermaid-svg-d0n6rUKAKd6x25nx .image-shape .label,#mermaid-svg-d0n6rUKAKd6x25nx .icon-shape .label{text-align:center;}#mermaid-svg-d0n6rUKAKd6x25nx .node.clickable{cursor:pointer;}#mermaid-svg-d0n6rUKAKd6x25nx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d0n6rUKAKd6x25nx .arrowheadPath{fill:#333333;}#mermaid-svg-d0n6rUKAKd6x25nx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d0n6rUKAKd6x25nx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d0n6rUKAKd6x25nx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d0n6rUKAKd6x25nx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d0n6rUKAKd6x25nx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d0n6rUKAKd6x25nx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d0n6rUKAKd6x25nx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d0n6rUKAKd6x25nx .cluster text{fill:#333;}#mermaid-svg-d0n6rUKAKd6x25nx .cluster span{color:#333;}#mermaid-svg-d0n6rUKAKd6x25nx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-d0n6rUKAKd6x25nx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d0n6rUKAKd6x25nx rect.text{fill:none;stroke-width:0;}#mermaid-svg-d0n6rUKAKd6x25nx .icon-shape,#mermaid-svg-d0n6rUKAKd6x25nx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d0n6rUKAKd6x25nx .icon-shape p,#mermaid-svg-d0n6rUKAKd6x25nx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d0n6rUKAKd6x25nx .icon-shape .label rect,#mermaid-svg-d0n6rUKAKd6x25nx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d0n6rUKAKd6x25nx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d0n6rUKAKd6x25nx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d0n6rUKAKd6x25nx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} pickle.dumps
序列化
Python 对象
func + args
字节流 bytes
b'\\x80\\x04...'
写入管道写端
os.write(pipe_w, data)
管道读端
os.read(pipe_r)
pickle.loads
反序列化
子进程还原出
func' + args'
形象说 :pickle 就像复印机 + 碎纸机 + 传真机:
- 父进程把函数和参数"复印"成二进制纸条
- 把纸条撕碎成字节流塞进管道
- 子进程从管道另一头取出字节,用同样的规则"拼回来"
2.9 代码层面的真相
当调用 p.start() 时,C 层代码实际在做:
python
# 伪代码:发生在 Python 解释器 C 源码里
import os
import pickle
# 父进程创建管道
read_fd, write_fd = os.pipe() # 想象成一根空心钢管,两头开口
# 父进程:把任务打包,塞进管子
task_package = pickle.dumps((target_function, args, kwargs))
os.write(write_fd, task_package) # 往管子里倒字节
# 子进程启动后(全新的空 Python)
os.exec*("python") # 新房间亮灯
# 子进程:从管子另一头取出
raw_bytes = os.read(read_fd, max_size) # 接包裹
func, args, kwargs = pickle.loads(raw_bytes) # 拆包裹
func(*args, **kwargs) # 终于开始干活
2.10 一个反直觉的事实
#mermaid-svg-B9bgvl7SzgVEaLHy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B9bgvl7SzgVEaLHy .error-icon{fill:#552222;}#mermaid-svg-B9bgvl7SzgVEaLHy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B9bgvl7SzgVEaLHy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B9bgvl7SzgVEaLHy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B9bgvl7SzgVEaLHy .marker.cross{stroke:#333333;}#mermaid-svg-B9bgvl7SzgVEaLHy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B9bgvl7SzgVEaLHy p{margin:0;}#mermaid-svg-B9bgvl7SzgVEaLHy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster-label text{fill:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster-label span{color:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster-label span p{background-color:transparent;}#mermaid-svg-B9bgvl7SzgVEaLHy .label text,#mermaid-svg-B9bgvl7SzgVEaLHy span{fill:#333;color:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy .node rect,#mermaid-svg-B9bgvl7SzgVEaLHy .node circle,#mermaid-svg-B9bgvl7SzgVEaLHy .node ellipse,#mermaid-svg-B9bgvl7SzgVEaLHy .node polygon,#mermaid-svg-B9bgvl7SzgVEaLHy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B9bgvl7SzgVEaLHy .rough-node .label text,#mermaid-svg-B9bgvl7SzgVEaLHy .node .label text,#mermaid-svg-B9bgvl7SzgVEaLHy .image-shape .label,#mermaid-svg-B9bgvl7SzgVEaLHy .icon-shape .label{text-anchor:middle;}#mermaid-svg-B9bgvl7SzgVEaLHy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B9bgvl7SzgVEaLHy .rough-node .label,#mermaid-svg-B9bgvl7SzgVEaLHy .node .label,#mermaid-svg-B9bgvl7SzgVEaLHy .image-shape .label,#mermaid-svg-B9bgvl7SzgVEaLHy .icon-shape .label{text-align:center;}#mermaid-svg-B9bgvl7SzgVEaLHy .node.clickable{cursor:pointer;}#mermaid-svg-B9bgvl7SzgVEaLHy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B9bgvl7SzgVEaLHy .arrowheadPath{fill:#333333;}#mermaid-svg-B9bgvl7SzgVEaLHy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B9bgvl7SzgVEaLHy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B9bgvl7SzgVEaLHy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B9bgvl7SzgVEaLHy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B9bgvl7SzgVEaLHy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B9bgvl7SzgVEaLHy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster text{fill:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy .cluster span{color:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-B9bgvl7SzgVEaLHy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B9bgvl7SzgVEaLHy rect.text{fill:none;stroke-width:0;}#mermaid-svg-B9bgvl7SzgVEaLHy .icon-shape,#mermaid-svg-B9bgvl7SzgVEaLHy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B9bgvl7SzgVEaLHy .icon-shape p,#mermaid-svg-B9bgvl7SzgVEaLHy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B9bgvl7SzgVEaLHy .icon-shape .label rect,#mermaid-svg-B9bgvl7SzgVEaLHy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B9bgvl7SzgVEaLHy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B9bgvl7SzgVEaLHy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B9bgvl7SzgVEaLHy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} pickle
父进程里的 worker 函数
id=0x7f8b1234
管道传输
子进程里的 worker' 函数
id=0x7f8c5678
注意:这是两个不同的函数对象!
只是代码逻辑相同
子进程还原出来的函数,内存地址完全不同 ,只是"长得一样"。就像你给朋友传真了一份文件,他手里那份和你手里那份不是同一张纸,只是内容相同。
2.11 一句话总结
spawn模式下,父子进程像两个互相看不见的封闭实验室 ,唯一的联络方式是一根细管子 。你要传递任何东西,都必须先pickle打成二进制包裹 ,塞过去让对方拆开。这保证了安全隔离,但也意味着不可 pickle 的东西(如打开的文件、数据库连接、lambda 函数)根本塞不进管子。
2.11 关键机制序列化与反序列化
spawn 的核心是 pickle 序列化。当你调用:
python
p = Process(target=my_func, args=(obj1, obj2))
p.start()
实际发生的是:
主进程: my_func + (obj1, obj2) ──pickle──> 字节流
↓ 通过管道传递
子进程: 字节流 ──unpickle──> 重建函数和对象 ──> 执行
这意味着:
- 所有
args、kwargs、实例属性都必须可被 pickle - 全局变量不会被自动继承(与 fork 完全不同)
2.12 关键知识点详解
2.12.1 if __name__ == '__main__' 为何必须?
在 spawn 模式下,子进程会重新导入模块。如果没有这个保护:
python
# bad_example.py
from multiprocessing import Process
def worker():
print("working")
# ⚠️ 子进程导入此模块时,下面代码会再次执行
p = Process(target=worker)
p.start() # 子进程再创建子进程... 无限递归!
正确写法:
python
# good_example.py
from multiprocessing import Process
def worker():
print("working")
if __name__ == '__main__':
p = Process(target=worker)
p.start()
p.join()
2.12.2 不可 pickle 对象的典型陷阱
python
from multiprocessing import Process
class MyClass:
def __init__(self):
self.lock = threading.Lock() # ❌ Lock 不可 pickle
self.db_conn = sqlite3.connect(":memory:") # ❌ 连接不可 pickle
def run(self):
pass
obj = MyClass()
p = Process(target=obj.run)
p.start() # TypeError: cannot pickle '_thread.lock' object
2.12. 3解决方案:__getstate__ / __setstate__
python
import threading
from multiprocessing import Process
class MyClass:
def __init__(self):
self.data = "important"
self.lock = threading.Lock() # 不可序列化
self._init_lock()
def _init_lock(self):
self.lock = threading.Lock()
def __getstate__(self):
"""序列化前调用:移除不可序列化属性"""
state = self.__dict__.copy()
del state['lock'] # 不序列化锁
return state
def __setstate__(self, state):
"""反序列化后调用:重建资源"""
self.__dict__.update(state)
self._init_lock() # 重新创建锁
def run(self):
with self.lock:
print(f"Safe: {self.data}")
if __name__ == '__main__':
obj = MyClass()
p = Process(target=obj.run)
p.start()
p.join()
3、完整实战示例
3.1 基础示例:传递数据
python
import os
from multiprocessing import Process, set_start_method
def worker(name, data_list):
print(f"[子进程 {os.getpid()}] 收到: name={name}, list={data_list}")
data_list.append(999) # ⚠️ 修改不会影响父进程(spawn不共享内存)
print(f"[子进程] 修改后: {data_list}")
if __name__ == '__main__':
set_start_method('spawn', force=True) # 显式设置
my_data = [1, 2, 3]
p = Process(target=worker, args=("test", my_data))
p.start()
p.join()
print(f"[父进程 {os.getpid()}] 最终: {my_data}") # [1, 2, 3]
3.2 使用 Pool 与 spawn
python
from multiprocessing import Pool, set_start_method
import os
def init_worker():
"""每个子进程启动时调用一次"""
print(f"Worker initialized in PID {os.getpid()}")
def square(x):
return x * x
if __name__ == '__main__':
set_start_method('spawn', force=True)
# initializer 在 spawn 模式下非常有用,用于重建资源
with Pool(processes=4, initializer=init_worker) as pool:
results = pool.map(square, range(10))
print(results)
3.3 Manager + spawn 的正确用法
python
from multiprocessing import Process, Manager, set_start_method
def worker(shared_dict, shared_list, q):
# 使用 Manager 创建的共享对象
shared_dict['pid'] = os.getpid()
shared_list.append('from_child')
q.put("message")
if __name__ == '__main__':
set_start_method('spawn', force=True)
manager = Manager()
d = manager.dict()
l = manager.list()
q = manager.Queue()
# ✅ 传递的是代理对象,可以被 pickle
p = Process(target=worker, args=(d, l, q))
p.start()
p.join()
print(f"dict: {dict(d)}")
print(f"list: {list(l)}")
print(f"queue: {q.get()}")
4、性能与最佳实践
| 场景 | 建议 |
|---|---|
| Windows 开发 | 默认 spawn,无需设置,但要确保代码兼容 |
| 跨平台兼容 | 始终使用 if __name__ == '__main__' |
| 大对象传递 | 避免频繁传递大对象,使用 Manager 或文件/内存映射 |
| 资源初始化 | 用 initializer 参数在每个子进程中初始化一次 |
| 调试 spawn 问题 | 设置 multiprocessing.log_to_stderr() 查看详细日志 |
4.1 性能对比代码
python
import time
from multiprocessing import Process, set_start_method
def noop():
pass
# spawn 模式
set_start_method('spawn', force=True)
start = time.perf_counter()
for _ in range(10):
p = Process(target=noop)
p.start()
p.join()
print(f"spawn 10次: {time.perf_counter() - start:.2f}s")
# 通常 2-5 秒
5、常见错误速查表
| 错误信息 | 原因 | 解决 |
|---|---|---|
TypeError: cannot pickle 'xxx' object |
参数/实例含不可序列化对象 | 实现 __getstate__/__setstate__ |
RuntimeError: An attempt has been made to start a new process... |
缺少 if __name__ == '__main__' |
加上保护 |
AttributeError: Can't get attribute 'xxx' |
子进程找不到函数定义 | 确保函数在顶级命名空间 |
EOFError / BrokenPipeError |
Manager 被提前关闭 | 确保 Manager 生命周期覆盖子进程 |
总结
spawn 模式的核心本质:
子进程是一个全新的、干净的 Python 进程 ,它通过 pickle 序列化 接收任务参数,然后从头重新初始化运行环境。
这个设计虽然牺牲了 fork 的速度优势,但换来了:
- 线程安全性(无锁继承问题)
- 跨平台一致性(Windows 可用)
- 可预测性(无全局状态污染)
如果你的代码需要在 Windows 上运行,或者涉及多线程混合编程,spawn 是唯一可靠的选择。