多进程加速蒙特卡洛树搜索时遇RLock继承错误求助
解决MCTS多进程推演中的RLock共享错误
问题根源
你遇到的RuntimeError: RLock objects should only be shared between processes through inheritance,本质是普通RLock是线程同步原语,无法在独立内存空间的进程间直接传递或共享。multiprocessing的进程不会共享父进程的内存,只有通过继承创建的子进程才能继承父进程的RLock,而你当前的实现应该是把包含RLock的游戏/MCTS节点对象直接传给了子进程,导致报错。
解决方案
方案1:重构推演逻辑,让进程独立处理(最优)
MCTS的推演(rollout)阶段本身不需要共享状态,每个进程可以独立处理游戏状态的副本,完成推演后返回结果,由主进程统一更新MCTS节点的统计信息。完全避免跨进程传递锁。
修改后的代码示例
MCTS.py
import random # 纯数据类的游戏状态,不含任何锁,支持复制 class GameState: def __init__(self): self.score = 0 self.done = False def copy(self): new_state = GameState() new_state.score = self.score new_state.done = self.done return new_state def step(self, action): if self.done: return self.score += random.randint(-1, 3) self.done = self.score >= 10 or self.score <= -5 def get_winner(self): if self.score >= 10: return 1 elif self.score <= -5: return -1 return 0 class MCTSNode: def __init__(self, state): self.state = state self.visits = 0 self.wins = 0 self.children = [] # 移除节点内的RLock,主进程单线程更新时无需锁 # 独立的推演函数,每个进程执行自己的推演 def rollout(state): current_state = state.copy() while not current_state.done: action = random.randint(0, 2) current_state.step(action) return current_state.get_winner()
launch.py
import multiprocessing as mp from MCTS import MCTSNode, GameState, rollout def main(): root_state = GameState() root_node = MCTSNode(root_state) num_simulations = 1000 num_processes = 4 # 用进程池批量执行推演任务 with mp.Pool(num_processes) as pool: # 每个任务传递独立的游戏状态副本 results = pool.map(rollout, [root_node.state.copy() for _ in range(num_simulations)]) # 主进程统一更新MCTS节点统计 for res in results: root_node.visits += 1 if res == 1: root_node.wins += 1 print(f"访问次数: {root_node.visits}, 获胜次数: {root_node.wins}, 胜率: {root_node.wins/root_node.visits:.2f}") if __name__ == "__main__": main()
方案2:使用进程安全的锁(仅当必须共享状态时)
如果你的游戏逻辑必须共享某些资源,需要用multiprocessing.Manager创建进程安全的RLock,而不是普通的RLock。Manager会在后台启动服务进程,让所有子进程通过代理访问锁。
代码示例
修改launch.py中的进程池初始化部分:
import multiprocessing as mp from multiprocessing import Manager from MCTS import MCTSNode, GameState, rollout # 初始化进程锁 def init_process(lock): global shared_lock shared_lock = lock # 带共享锁的推演函数(仅当需要共享资源时使用) def rollout_with_lock(state): current_state = state.copy() while not current_state.done: with shared_lock: # 这里处理需要共享的逻辑,比如全局资源访问 action = random.randint(0, 2) current_state.step(action) return current_state.get_winner() def main(): root_state = GameState() root_node = MCTSNode(root_state) num_simulations = 1000 num_processes = 4 manager = Manager() shared_lock = manager.RLock() # 初始化进程池时传递锁 with mp.Pool(num_processes, initializer=init_process, initargs=(shared_lock,)) as pool: results = pool.map(rollout_with_lock, [root_node.state.copy() for _ in range(num_simulations)]) # 主进程更新节点 for res in results: root_node.visits += 1 if res == 1: root_node.wins += 1 print(f"访问次数: {root_node.visits}, 获胜次数: {root_node.wins}, 胜率: {root_node.wins/root_node.visits:.2f}") if __name__ == "__main__": main()
注意事项
- 尽量优先用方案1,因为进程间共享锁会带来性能开销,而MCTS推演阶段天然适合并行化独立处理。
- 所有跨进程传递的对象必须是可序列化的(pickle支持),所以游戏状态类要避免包含不可序列化的属性(比如普通锁、文件句柄等)。
内容的提问来源于stack exchange,提问作者ets_ets
相关产品推荐
相关产品推荐

