Windows下Python concurrent.futures并发重复执行问题解决方法
问题根因
- 首先你混淆了多线程和多进程:代码中使用的
ProcessPoolExecutor是多进程池,并非多线程池。Windows系统下Python多进程默认采用spawn启动方式,和Linux/macOS默认的fork机制不同:spawn不会直接复制父进程的内存状态,而是会启动全新的Python解释器进程,重新导入执行主脚本的所有顶层(函数/类外、不受__name__判断保护)代码,以此加载目标函数、依赖变量等运行必需的资源。 - 你当前的写法仅将进程池启动逻辑放入了
if "__main__" == __name__:判断块,待处理列表生成、计时变量初始化、结果打印、结果字典初始化这些逻辑都暴露在顶层。每启动一个子进程,这些顶层代码就会被执行一次,这就是你看到重复打印、子进程中字典长度为0的原因——子进程导入脚本时__name__不是"__main__",不会进入判断块执行多进程任务逻辑,自然输出空字典、零耗时的结果。 - 这个重复现象不是递归问题,是
spawn启动机制下顶层代码未做隔离的必然结果。另外你使用的process_time()仅统计当前进程的CPU时间,不会累加子进程的CPU耗时,统计多进程任务的整体运行时间结果不准。
修复方案
核心规则:所有仅需要主进程执行的逻辑,必须全部放入if "__main__" == __name__:判断块内部,顶层仅保留导入语句、全局常量、函数/类定义这类所有进程都需要加载的公共资源。
修复后的可运行代码如下:
import concurrent.futures from itertools import product from time import perf_counter # 多进程调用的目标函数属于公共定义,放在顶层供所有进程加载 def genDict(in_value): out_dict = {} out_dict[in_value] = list(in_value) return out_dict # 仅主进程需要执行的逻辑,全部放入判断块内 if __name__ == "__main__": # 生成待处理数据集 alphabets = ['a', 'b', 'c', 'd', 'e'] listToProcess = [''.join(i) for i in product(alphabets, repeat=4)] print('待处理列表长度:', len(listToProcess)) # 初始化结果容器、计时器 dictResult = {} t1_start = perf_counter() # 启动多进程池执行任务 with concurrent.futures.ProcessPoolExecutor(4) as executor: futures = [executor.submit(genDict, elem) for elem in listToProcess] # 用as_completed在任务完成时立刻获取结果,提升执行效率 for future in concurrent.futures.as_completed(futures): dictResult.update(future.result()) t1_stop = perf_counter() print(f'多进程执行耗时 = {t1_stop - t1_start} 秒') print('\n该打印逻辑仅在主进程运行,不会重复触发') print(f'结果字典长度: {len(dictResult)}')
额外说明
- 如果你实际需要的是多线程而非多进程,将
ProcessPoolExecutor替换为ThreadPoolExecutor即可。多线程共享主进程内存空间,不会重新导入脚本,Windows下也不需要__name__判断即可正常运行,不会出现重复执行问题。 - 并行任务耗时统计推荐使用
perf_counter(),它统计的是实际经过的墙钟时间,比process_time()更适合衡量并行任务的整体执行速度。
内容的提问来源于stack exchange,提问作者Jose R
相关产品推荐
相关产品推荐

