使用Pool.starmap多进程处理时持续触发索引越界错误
问题分析与解决方案
核心问题原因
你遇到的"List Index out of range"错误,本质是多进程内存隔离+不合理的列表修改逻辑导致的:
- 多进程的内存空间是独立的,子进程里对
snakes、nets、ge执行pop(x),只会修改当前进程的列表副本,主进程和其他子进程的列表完全不受影响。当某个子进程的列表被缩短后,后续再用原索引x访问就会触发越界。 - 子进程修改
ge[x].fitness无法同步到主进程,NEAT算法根本拿不到正确的适应度值,完全违背训练逻辑。 max_score是不可变类型,子进程里的修改不会同步到主进程,这个变量的更新毫无意义。
修复后的代码
重构子进程函数
让每个进程只处理自己对应的个体,不需要传递整个列表,避免索引依赖:
def snake_process(snake, net, genome): print("start process") process_max_score = 0 total_fitness = 0 while True: state_old, dis_old = agent.get_state(snake) output = net.activate(state_old) final_move = [0, 0, 0] final_move[np.argmax(output)] = 1 reward, done, score = snake.play_step(final_move) if score > process_max_score: process_max_score = score total_fitness += reward # 累积适应度,而非单次覆盖 if done: snake.reset() return total_fitness, process_max_score
修改主进程多进程逻辑
由主进程统一收集子进程的计算结果,再更新全局状态:
def main(genomes, config): nets =[] ge = [] snakes = [] max_score = 0 for _, g in genomes: net = neat.nn.FeedForwardNetwork.create(g, config) nets.append(net) snakes.append(Snake(BLOCK_WIDTH*BLOCKS, BLOCK_WIDTH*BLOCKS)) g.fitness = 0 ge.append(g) num_cores = mp.cpu_count() with mp.Pool(num_cores) as pool: # 每个进程只接收自己需要的单个对象 args = zip(snakes, nets, ge) # 收集所有进程返回的(总适应度, 进程内最高得分) results = pool.starmap(snake_process, args) # 主进程统一更新全局状态 for idx, (fitness, process_max) in enumerate(results): ge[idx].fitness = fitness if process_max > max_score: max_score = process_max
关键修改说明
- 移除索引依赖:每个子进程只处理自己的
snake、net、genome,彻底避免索引越界问题。 - 主进程统一更新:子进程返回计算结果,由主进程负责更新genome适应度和全局最高得分,保证NEAT算法能获取到正确的训练数据。
- 删除无效列表修改:子进程不再执行
pop操作,主进程保留所有个体对象用于后续的NEAT进化选择(繁殖、淘汰等逻辑)。
额外注意事项
- 确保
Snake、neat.nn.FeedForwardNetwork等对象支持pickle序列化(multiprocessing传递参数需要序列化),如果Snake类有不可序列化的属性(比如GUI组件),需要调整类的设计。 - 永远不要在子进程中修改全局共享状态,所有状态更新都由主进程完成,避免并发冲突和内存隔离导致的无效操作。
内容的提问来源于stack exchange,提问作者Junwoo Hong
相关产品推荐
相关产品推荐

