Python多进程操作共享列表时遭遇索引越界及代理对象不可下标问题求助
Python多进程操作共享列表时遭遇索引越界及代理对象不可下标问题求助
兄弟,我太懂你从C转Python多进程的困惑了——毕竟C线程是完全共享进程内存的,但Python的多进程(尤其是基于multiprocessing模块的)完全是另一个路数,咱们一步步拆解你的问题:
第一个问题:为什么会出现IndexError: list index out of range?
你在主进程里初始化了testarr,但Python的每个子进程都有独立的内存空间,不会共享主进程的普通列表:
- 在Unix/Linux系统下,子进程通过
fork创建,虽然会复制父进程内存,但你的initFunction是在主进程的if __name__ == '__main__':块里执行的,子进程启动时不会走这个代码块,所以子进程里的testarr还是空列表。 - 在Windows系统下,子进程会重新导入你的脚本,这时候
testarr会被重新初始化为空列表,自然访问任何索引都会越界。
你看到报错固定在某个索引(比如93750),只是因为那个索引是子进程分到的第一个任务,但本质上所有子进程里的testarr都是空的。
第二个问题:为什么用Manager后出现TypeError: 'AutoProxy[list]' object is not subscriptable?
你的方向完全正确——multiprocessing.Manager就是用来创建跨进程共享对象的,但你的代码有两个小坑:
Manager.list创建的代理列表本身支持下标访问,但你列表里的字典是普通对象,修改后不会自动同步到共享列表。- 用
Process批量创建10万个进程本身就非常低效,而且容易出代理对象的偶发问题。
正确的解决代码(两种常用方案)
方案1:用Manager+Pool(高效适合大量任务)
用Pool的初始化函数把共享列表传递给所有子进程,避免重复传参:
import time from multiprocessing import Pool, Manager def init_worker(shared_arr): # 把共享列表设为子进程的全局变量,方便任务函数直接访问 global testarr testarr = shared_arr def checkForEvenAtIndex(index=0): item = testarr[index] if int(item["value"]) % 2 == 0: item["flag"] = 1 # 必须把修改后的字典放回共享列表,否则同步失效 testarr[index] = item return None if __name__ == '__main__': start = time.time() cpu = 4 length = 100000 with Manager() as manager: testarr = manager.list() # 主进程初始化共享列表 for i in range(length): testarr.append({"value": str(i), "flag": 0}) # 进程池初始化时传递共享列表 with Pool(cpu, initializer=init_worker, initargs=(testarr,)) as pool: pool.map(checkForEvenAtIndex, range(length)) end = time.time() print('Total time taken *****' + str(end - start))
方案2:无共享内存的最佳实践(效率更高)
其实你的任务完全可以不需要共享内存——让子进程返回计算结果,主进程最后组装数据,这是Python多进程的常用最优写法:
import time from multiprocessing import Pool def checkForEvenAtIndex(index): # 直接返回当前索引的flag值,不需要共享任何数据 return 1 if index % 2 == 0 else 0 if __name__ == '__main__': start = time.time() cpu = 4 length = 100000 with Pool(cpu) as pool: # 批量获取所有索引的计算结果 flags = pool.map(checkForEvenAtIndex, range(length)) # 主进程统一组装成目标列表 testarr = [{"value": str(i), "flag": flags[i]} for i in range(length)] end = time.time() print('Total time taken *****' + str(end - start))
这种方式完全避免了跨进程共享的开销,速度会比用Manager快很多。
给C++转Python开发者的关键提醒
- Python多进程≠C++线程:C++线程共享进程内存,Python多进程是独立进程,内存默认完全隔离,必须用
Manager、Array等专门的共享对象。 if __name__ == '__main__':是生命线:这个代码块只有主进程会执行,子进程不会运行,所有子进程需要的共享数据初始化不能放在这里面(除了主进程自己的逻辑)。- 共享对象有开销:跨进程共享数据需要通过IPC(进程间通信)实现,速度远不如普通内存操作,能不用共享就尽量不用。
备注:内容来源于stack exchange,提问作者Don Woodward
相关产品推荐
相关产品推荐

