MacOS下Python3.9对比3.7 multiprocessing重复执行问题修复
问题根因
该现象不是代码bug,是Python 3.8及之后版本在macOS平台调整multiprocessing默认进程启动方式导致的:
- Python 3.7及更早版本,macOS下
multiprocessing默认使用fork模式创建子进程:该模式直接复制主进程当前内存状态生成子进程,不会从头重新执行主脚本代码。 - Python 3.8开始,macOS平台默认启动方式改为
spawn模式:该模式会启动全新的Python解释器进程,重新导入整个主脚本完成初始化,所有写在if __name__ == '__main__'判断块外的顶层代码,都会被每个子进程执行一次。8核CPU创建进程池时默认拉起8个工作进程,就会出现8次顶层代码重复执行的现象。
修复方案
优先推荐使用标准兼容写法,不建议强行改回fork模式:
- 把所有仅需要主进程执行的逻辑,全部放到
if __name__ == '__main__'保护块内部,避免子进程导入脚本时触发执行。比如原代码中第5行的顶层打印、脚本最后一行的退出打印,都属于这类逻辑,移入保护块即可解决重复执行问题。 - 如果确实需要兼容旧逻辑,可以在创建进程池之前显式设置进程启动方式为fork,注意:该设置全局仅能调用一次,且macOS后续系统版本已废弃fork相关API,强行使用可能触发进程卡死、系统调用异常等问题,非必要不选择该方案:
import multiprocessing as mp # 该行必须放在所有创建进程/进程池的逻辑之前 mp.set_start_method('fork')
修正后的可运行代码示例:
#!/usr/bin/python from time import strftime import multiprocessing as mp # 函数定义放在顶层无影响,子进程导入时仅完成函数声明不会执行 def pullLast(): print(12345678998765432345678) return 1 def mprocULwork(uplist): ULsyms = [e for e in uplist if e is not None] p_size = mp.cpu_count() pool = mp.Pool(processes=p_size, maxtasksperchild=400) pool_outputs = pool.map(ULProcess, ULsyms) pool.close() pool.join() del pool print (strftime("%H:%M:%S"), "Finished ULwork") def ULProcess(symbol): pSet = 0 print(pSet,symbol) if __name__ == '__main__': # 所有主进程专属执行逻辑全部放在该块内 print(strftime("%H:%M:%S") + ' Run line5') pSet = 1 symlist = ["G","Y","S"] ullist = symlist global lastPriceDF lastPriceDF = pullLast() mprocULwork(ullist) print (strftime("%H:%M:%S"), "post") print (strftime("%H:%M:%S"), 'Exiting....line last')
threading模块相关说明
threading模块不存在同类问题:
- 线程是运行在同一个进程内部的执行单元,所有线程共享当前进程的内存空间和初始化状态,启动线程时不会启动新的解释器进程,也不会重新导入执行主脚本,无论哪个Python版本都不会出现这类顶层代码重复执行的问题。
- Python 3.7到3.9版本的
threading模块没有涉及启动逻辑的重构,不存在同类行为变更。
内容的提问来源于stack exchange,提问作者Merlin
相关产品推荐
相关产品推荐

