如何解决Python Multiprocessing无法修改原列表并返回结果的问题?
Python多进程无法修改原列表的原因及解决方法
问题描述
我想编写一个利用Python multiprocessing 包加速列表读写操作的程序,但遇到了多进程无法修改原列表的问题——传入的参数似乎被克隆了,子进程修改的只是副本,原列表完全没变化。有没有办法让多进程引用同一个列表?
示例代码
import multiprocessing from multiprocessing import get_context list_of_files_1 = ['aa','aaba','aaaabb'] list_of_files_2 = ['aa','aaba','aaaabb'] def process(file_list,index): count = 0 for i in file_list[index]: if i == 'a': count +=1 file_list[index]=count print(f"Value in list @ index {index}: {file_list[index]}\t Memory: {id(file_list)}") # 串行处理 for i in range(len(list_of_files_1)): process(list_of_files_1,i) # 并行处理 p = get_context("fork").Pool(processes=3) for i in range(len(list_of_files_2)): p.apply(process,[list_of_files_2,i])
输出结果
OUTPUT: memory locations: file_1: 4410192704 file_2: 4438218048 Value in list @ index 0: 2 Memory Loc: 4410192704 Value in list @ index 1: 3 Memory Loc: 4410192704 Value in list @ index 2: 4 Memory Loc: 4410192704 New list of files before serial process:[2, 3, 4] Value in list @ index 0: 2 Memory Loc: 4438530624 Value in list @ index 1: 3 Memory Loc: 4438327168 Value in list @ index 2: 4 Memory Loc: 4438405440 New list of files before serial process:['aa', 'aaba', 'aaaabb']
串行处理后列表正常修改,并行处理后原列表无变化。
原因分析
Python多进程中,每个子进程都有独立的内存空间。当你把列表作为参数传给子进程时,会通过pickle序列化复制一份到子进程内存中,子进程修改的只是这份副本,完全不影响主进程里的原列表。从输出的内存ID也能看出,每个子进程的列表ID都不同,说明都是独立的副本。
解决方法
方法1:使用进程间共享内存对象
multiprocessing.Manager 提供了可以跨进程共享的列表类型,它会在后台启动一个管理进程,协调所有子进程对共享数据的访问。
import multiprocessing from multiprocessing import Manager def process(file_list, index): count = 0 for i in file_list[index]: if i == 'a': count +=1 file_list[index] = count print(f"Value in list @ index {index}: {file_list[index]}\t Memory: {id(file_list)}") # 用Manager创建共享列表 with Manager() as manager: list_of_files = manager.list(['aa','aaba','aaaabb']) p = multiprocessing.Pool(processes=3) for i in range(len(list_of_files)): p.apply(process, [list_of_files, i]) p.close() p.join() print(f"修改后的列表: {list(list_of_files)}")
方法2:子进程返回结果,主进程统一更新
这种方式不需要共享内存,让子进程完成计算后返回结果,主进程再把结果批量更新到原列表里,逻辑简单且避免了共享数据的同步问题。
import multiprocessing def process(item): count = 0 for i in item: if i == 'a': count +=1 return count list_of_files = ['aa','aaba','aaaabb'] p = multiprocessing.Pool(processes=3) # 用map批量处理所有元素,获取结果列表 results = p.map(process, list_of_files) p.close() p.join() # 主进程更新原列表 list_of_files = results print(f"修改后的列表: {list(list_of_files)}")
方法3:利用fork的写时复制(仅Unix/Linux/macOS)
你使用了fork上下文,在Unix类系统中,fork创建的子进程会继承父进程的内存空间,但写时复制机制会在子进程修改数据时复制副本,所以直接修改原列表依然无效。不过可以用multiprocessing.Array或ctypes创建真正的共享内存数组,但这种方式跨平台性差,不如前两种通用。
总结
- 若只是简单计算后更新列表,优先用方法2,逻辑清晰无同步风险;
- 若需要多进程实时共享修改数据,再用方法1的
Manager.list(); - 方法3仅适合Unix类系统,不推荐跨平台场景使用。
内容的提问来源于stack exchange,提问作者Alex21001
相关产品推荐
相关产品推荐

