Python multiprocessing中列表推导式传入列表无法更新问题咨询
多进程下列表推导式生成的列表无法更新字典值的问题
当使用multiprocessing模块时,通过列表推导式生成的列表传入函数后,其中字典的val2chg条目无法更新。但不使用多进程时逻辑正常,直接传入原列表而非列表推导式生成的列表时,多进程代码也能成功执行。
输入与测试场景结果
输入的原始列表sld为:
[{'val': 0, 'val2chg': 'not'}, {'val': 1, 'val2chg': 'not'}]
四种测试场景的结果:
- multiproc: True. listcomp: True, [{'val': 0, 'val2chg': 'not'}, {'val': 1, 'val2chg': 'not'}]
- multiproc: False. listcomp: True, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]
- multiproc: False. listcomp: False, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]
- multiproc: True. listcomp: False, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]
测试代码
import multiprocessing as mp sld = [{'val':0, 'val2chg':'not'}, {'val':1, 'val2chg':'not'}] print(sld) def update_values(sld, domp): global update def update(idx, some): some['val2chg'] = 'changed' return idx, some if domp: pool = mp.Pool() results = [pool.apply_async(update, (idx, sld[idx])) for idx in range(len(sld))] pool.close() pool.join() for result in results: idx = result.get()[0] sld[idx] = result.get()[1] else: for idx in range(len(sld)): results = update(idx, sld[idx]) idx = results[0] sld[idx] = results[1] domp = True listcomp = False if listcomp: update_values([s for idx, s in enumerate(sld)], domp) else: update_values(sld, domp) print(f'multiproc: {domp}. listcomp: {listcomp}, {sld}')
问题原因与解决方法
原因分析
- 多进程内存隔离:Python多进程采用写时复制机制,每个子进程会复制父进程的内存空间。当你用列表推导式
[s for idx, s in enumerate(sld)]生成新列表时,这个列表是主进程的临时局部变量,传入update_values后:- 子进程处理时会复制这个临时列表,更新操作只作用于子进程内的副本,主进程的原始
sld完全不受影响。 - 函数执行完后,临时列表被销毁,主进程自然看不到任何变化。
- 子进程处理时会复制这个临时列表,更新操作只作用于子进程内的副本,主进程的原始
- 直接传原列表的逻辑:直接传原
sld时,update_values里的赋值操作是在主进程完成的——子进程返回处理后的字典,主进程把它替换到原列表的对应位置,所以原列表能被更新。
解决方法
如果要保留列表推导式的使用,同时让主进程的原始列表得到更新,可修改代码让函数返回处理后的列表,再赋值给原sld:
- 修改
update_values函数,在末尾添加返回语句:
def update_values(sld, domp): global update def update(idx, some): some['val2chg'] = 'changed' return idx, some if domp: pool = mp.Pool() results = [pool.apply_async(update, (idx, sld[idx])) for idx in range(len(sld))] pool.close() pool.join() for result in results: idx = result.get()[0] sld[idx] = result.get()[1] else: for idx in range(len(sld)): results = update(idx, sld[idx]) idx = results[0] sld[idx] = results[1] return sld # 添加返回处理后的列表
- 修改调用逻辑,接收返回值:
if listcomp: sld = update_values([s for idx, s in enumerate(sld)], domp) else: update_values(sld, domp)
或者直接避免使用临时的列表推导式生成列表,直接操作原列表,让主进程直接接收子进程的更新结果。
内容的提问来源于stack exchange,提问作者Schro
相关产品推荐
相关产品推荐

