如何并行运行多个带独立进程的simpy.Environment仿真环境
Simpy多仿真环境并行运行实现方案
问题根因
simpy.Environment本身是单线程驱动的事件循环模型,直接在主程序里用for循环挨个实例化、调用env.run()时,单个环境的事件循环会一直占住当前线程的执行权,直到仿真完全结束才会返回,本质就是串行执行,自然会阻塞其他环境启动。
可行实现方案
最适配DRL训练采样场景的方案是多进程隔离运行,不要用多线程(受Python GIL限制,CPU密集的仿真逻辑无法真正并行,额外开销大),具体实现逻辑如下:
- 每个
simpy.Environment实例完全在独立的子进程内初始化、运行,不同进程的内存空间完全隔离,事件循环互不干扰,可被操作系统调度到不同CPU核心实现真正并行 - 进程间通过队列传递DRL交互需要的控制指令、观测、动作、奖励、结束标志等数据
- 可以自己实现进程通信逻辑,也可以直接复用现有DRL框架的并行环境封装,减少重复代码
最简实现参考
import simpy from multiprocessing import Process, Queue # 以下两个函数替换成你自己的仿真逻辑 def reset_env(env, config): """重置仿真环境,返回初始观测""" # 清空原有进程,重新绑定仿真实体、自定义进程 pass def step_env(env, action, step_duration=1): """执行动作,步进仿真step_duration时长,返回(观测, 奖励, 结束标志)""" # 把动作写入仿真对应实体的状态,驱动env运行step_duration时长 # 统计奖励、判断是否触发终止条件 pass def env_worker(config, action_q, obs_q, seed): """子进程内运行的单仿真环境工作逻辑""" # 子进程内部独立初始化simpy环境,避免跨进程传环境对象 env = simpy.Environment() env._seed = seed # 初始化自定义仿真进程 while True: cmd, data = action_q.get() if cmd == "reset": obs = reset_env(env, config) obs_q.put(obs) elif cmd == "step": obs, reward, done = step_env(env, data) obs_q.put((obs, reward, done)) elif cmd == "close": break if __name__ == "__main__": PARALLEL_ENV_NUM = 8 # 建议和CPU核心数匹配 base_config = {} # 你的仿真统一配置 action_queues, obs_queues, procs = [], [], [] # 启动所有并行环境子进程 for env_idx in range(PARALLEL_ENV_NUM): act_q, ob_q = Queue(), Queue() p = Process(target=env_worker, args=(base_config, act_q, ob_q, env_idx)) p.start() procs.append(p) action_queues.append(act_q) obs_queues.append(ob_q) # DRL训练循环逻辑示例 # 给所有环境发重置指令 for aq in action_queues: aq.put(("reset", None)) init_obs_list = [oq.get() for oq in obs_queues] # 训练时批量发动作、收结果即可,所有环境在子进程内并行运行 # 训练结束后记得关闭所有子进程 for aq in action_queues: aq.put(("close", None)) for p in procs: p.join()
注意事项
- 不要尝试在单进程内轮询调度多个simpy环境的事件循环(比如每次让每个env跑1个仿真步长再切换),本质还是串行执行,调度开销远大于收益
- 所有仿真相关的自定义类、函数要支持序列化,不要把无法序列化的对象(打开的文件句柄、线程锁、数据库连接等)从主进程传到子进程,这类资源要在子进程内部初始化
- 每个并行环境要设置独立的随机种子,避免采样轨迹相关性过高,影响on-policy DRL算法的收敛效果
- 如果用Stable Baselines3等现成DRL框架,只需要把单simpy环境封装成标准Gym接口,直接调用框架自带的
SubprocVecEnv即可自动完成多进程并行封装,不需要自己写进程通信逻辑
内容的提问来源于stack exchange,提问作者amin zak
相关产品推荐
相关产品推荐

