You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多进程处理嵌套对象实现遗传算法2D平台游戏种群并行更新问题

问题根本原因

  1. Python多进程默认进程隔离,你直接将Player对象传给子进程时,仅会序列化对象本身的属性,不会同步主进程的全局变量。你的Player.update方法依赖全局的groups.floor_tiles、settings等变量,子进程内没有初始化这些全局资源,自然会出现属性缺失、空对象报错。
  2. multiprocessing.Array仅支持存储C兼容的基础类型(int、float等),不能直接存放自定义Python对象,你强制传入对象类型就会触发类型报错。
  3. 你定义的update_players需要接收ret_queue参数,但创建进程时没有传这个参数,本身就存在传参不匹配的问题。

可行解决方案

优先用队列做结果回传,不需要折腾复杂的共享内存。遗传算法的种群更新本身就是跑完一轮再统一回收所有个体状态,序列化开销完全可控,改造成本最低。

步骤1:修改update_players函数

移除全局变量依赖,所有需要的资源都通过参数显式传入,处理完批次后统一返回结果:

def update_players(players_split, floor_tiles, config, goal, ret_queue):
    updated = []
    for p in players_split:
        if not p.is_dead and not p.reached_goal and not p.finished:
            if p.is_ai:
                p.releaseLeft()
                p.releaseRight()
                if p.brain_step >= len(p.brain.instructions):
                    if not p.reached_goal and not p.isMoving():
                        p.finished = True
                else:
                    p.executeNextBrainStep()

            p.rect.y += p.y_spd
            p.y_spd += p.gravity

            for tile in floor_tiles:
                if helpers.rectsColliding(p.rect, tile.rect):
                    # 垂直碰撞检测
                    if p.rect.getCenterY() < tile.rect.y:
                        p.y_spd = 0
                        p.rect.y = tile.rect.y - p.rect.height + 1
                        p.is_jumping = False
                    elif p.rect.getCenterY() > (tile.rect.y + tile.rect.height):
                        p.y_spd = 0
                        p.rect.y = tile.rect.y + tile.rect.height
                    # 水平碰撞检测
                    if (p.rect.getCenterY() >= tile.rect.getCenterY() - tile.rect.height/3 
                        and p.rect.getCenterY() <= tile.rect.getCenterY() + tile.rect.height/3):
                        if p.rect.getCenterX() < tile.rect.x:
                            p.right = 0
                            p.rect.x = tile.rect.x - p.rect.width
                        elif p.rect.getCenterX() > (tile.rect.x + tile.rect.width):
                            p.left = 0
                            p.rect.x = tile.rect.x + tile.rect.width

            p.dir = p.right - p.left
            p.rect.x += p.dir * p.walk_spd

            # 死亡判定
            if p.rect.x > config.SCR_W or (p.rect.x + p.rect.width) < 0:
                p.is_dead = True
                p.finished = True
            if p.rect.y > config.SCR_H or (p.rect.y + p.rect.height) < 0:
                p.is_dead = True
                p.finished = True

            # 最优距离判定
            if config.OPTIMIZATION_FITNESS:
                self_dist = helpers.dist_modular(p.rect.x, goal.rect.x, p.rect.y, goal.rect.y)
                best_dist = helpers.dist_modular(config.BEST_X, goal.rect.x, config.BEST_Y, goal.rect.y)
                if self_dist < best_dist:
                    p.finished = True
                    p.reached_goal = True

            # 终点判定
            if helpers.objectsColliding(p, goal):
                p.reached_goal = True
                p.finished = True
        updated.append(p)
    ret_queue.put(updated)

步骤2:修改主进程调用逻辑

Windows系统必须把多进程启动逻辑放在if __name__ == '__main__'块下,否则会反复启动进程报错:

import multiprocessing as mp
import numpy as np

if __name__ == '__main__':
    # 提前提取所有子进程需要的资源,避免依赖全局变量
    all_players = groups.players_group
    floor_tiles = groups.floor_tiles
    config = settings
    goal = settings.goal

    splits = np.array_split(all_players, config.PROCESSES)
    ret_queue = mp.Queue()
    processes = []

    for split in splits:
        p = mp.Process(
            target=update_players,
            args=(split, floor_tiles, config, goal, ret_queue)
        )
        processes.append(p)
        p.start()

    # 回收所有更新后的玩家对象
    updated_players = []
    for _ in range(config.PROCESSES):
        updated_players.extend(ret_queue.get())

    # 等待所有进程退出
    for p in processes:
        p.join()

    # 替换原种群为更新后的结果
    groups.players_group = updated_players

性能优化可选方案

如果后续Player对象过大、序列化开销太高,再考虑共享内存方案:把所有玩家的状态字段拆成独立的基础类型共享数组(比如所有玩家的x坐标存在一个Array('i', 玩家总数)里,y坐标存在另一个里,is_dead标记存在布尔数组里),子进程直接修改共享数组的对应位置,最后主进程再把数组值同步回Player对象即可,这个方案性能更高但改造成本更大,队列方案够用的情况下不需要优先考虑。

内容的提问来源于stack exchange,提问作者Santy Arellano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 18:36:08