如何将字典传递给concurrent.futures执行器,解决多进程下全局字典无写入问题
问题根本原因
你使用的ProcessPoolExecutor是多进程实现,每个子进程拥有独立的内存地址空间,主进程定义的全局data字典和子进程里的data是完全独立的两份拷贝,子进程对字典的修改只会作用在子进程自身的内存中,不会同步到主进程,所以最终主进程打印的data是空的。
解决方案
不需要用全局变量传递结果,改为让任务函数返回计算结果,主进程收集所有返回值后组装成字典即可,修改逻辑如下:
- 调整
shan函数,删除修改全局data的逻辑,改为返回文件名和对应的计算结果 - 将子进程需要用到的
samples变量作为参数传入shan函数(原代码中子进程无法直接读取主进程的samples全局变量,会触发未定义报错) - 主进程遍历所有执行完成的future,取出返回值写入主进程的
data字典
修改后的可运行代码示例
import collections import gzip import sys import glob import concurrent.futures from scipy.stats import entropy from Bio import SeqIO # 原熵计算函数无需修改 def estimate_shannon_entropy(dna_sequence): bases = collections.Counter([tmp_base for tmp_base in dna_sequence]) dist = [x/sum(bases.values()) for x in bases.values()] entropy_value = entropy(dist, base=2) return entropy_value # 修改shan函数:新增samples参数,返回键值对而非修改全局变量 def shan(i, samples): name1 = i.split("/")[-1] ext1 = name1.split(".")[-1] print(name1) if ext1 == "gz": f = gzip.open(i, 'rt') k = name1.split(".")[-2] else: f = open(i, 'r') # 修复原代码ext未定义的问题 k = ext1 if k[-1] == "a": fmt = "fasta" if k[-1] == "q": fmt = "fastq" c = 0 shannon_total = 0 for x in SeqIO.parse(f, fmt): c += 1 if c <= samples: shannon = estimate_shannon_entropy(str(x.seq)) shannon_total += shannon ans = float(shannon_total / samples) return name1, ans if __name__ == "__main__": folder = sys.argv[1] filelist = glob.glob(folder) # 保留你原有的tokenize排序逻辑即可 filelist.sort(key=tokenize) samples = int(sys.argv[2]) threads = int(sys.argv[3]) data = {} executor = concurrent.futures.ProcessPoolExecutor(threads) # 提交任务时显式传入samples参数 futures = [executor.submit(shan, i, samples) for i in filelist] concurrent.futures.wait(futures) # 收集所有子进程返回的结果,写入主进程的data字典 for future in futures: name, res = future.result() data[name] = res print(data)
额外注意事项
- 多进程代码的入口必须加
if __name__ == "__main__":防护,否则Windows系统下会无限递归启动进程报错 - 不要在多进程场景下依赖全局变量传递状态/结果,所有子进程需要的参数都应该显式传入,结果都应该显式返回
内容的提问来源于stack exchange,提问作者suze1992
相关产品推荐
相关产品推荐

