1

我正在使用opengl-es在android中制作游戏,使用多个线程:

class World{

    protected static final AtomicInteger entityLock = new AtomicInteger();

    private GameEntity entities[];

    public World(){
        // populate game world with entities
        // executed on main thread
        addEntity(new GameEntity("tank"));
        addEntity(new GameEntity("rifleman"));
        addEntity(new GameEntity("rifleman"));
    }

    void update(){
        synchronized(entityLock){
           for(int i = 0;i<entities.length;i++){
                // move entity to new position
                // executed on PhysThread
                entities[i].updatePosition();                    
            }
        }
        if(entity.isDead(){
            // remove entity. Enter sync block inside removeEntity() method
            removeEntity(entity);                  
        }                      
    }

    void draw(GL10 gl){
        synchronized(entityLock){
            for(int i = 0;i<entites.length;i++){
                // draw models
                // executed on GLThread
                Vector3 entityPosition = entities[i].getPosition();
                gl.glTranslatef(entityPosition.x, entityPosition.y, entityPosition.z);
                entities[i].draw();
            }
        }
    }

    public void addEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

    public void removeEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

} 

class MyRenderer implements GLSurfaceView.Renderer{

    World world;

    public MyRenderer(World world){
        this.world = world;
    }


    public void onDrawFrame(GL10 gl) {
        // executed on GLThread
        world.draw(gl);             
    }


}

class PhysThreadRunnable implements Runnable{

    private long tickRate = 30;

    private World world;

    private PhysThreadRunnable(World world){
        this.world = world;
    }

    protected void setTickRate(long tickRate){
        this.tickRate = tickRate;
    }

    public void run() {
        while(true){                
            try {
                // executed on PhysThread
                world.update();
                Thread.sleep(1000/tickRate);
            } catch (InterruptedException e) {
                return;
            }

        }
    }
}

MyActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        World world = new World(); 
        // sets up the game world, populates it with entities

        // set up GLSurfaceView (simplified)
        setContentView(R.layout.main);
        GLSurfaceView mGLView = findViewById(R.id.myGLSurfaceView);
        mGLView.setRenderer(new MyRenderer(world));

        // start phys thread
        PhysThreadRunnable physThreadRunnable = new PhysThreadRunnable(world);
        Thread physThread = new Thread(physThreadRunnable);
        physThread.start();
    }
}

我有一个问题,有时(但不是每次)当我开始游戏时,PhysThread 卡住等待释放锁(即当我去调试并暂停线程时,它只是坐在synchronized(entityLock)里面update()

真正奇怪的是,一段时间后(2 秒到 1 分钟之间),PhysThread 将被解除阻塞,游戏将继续进行,而任何一个线程都不会在线程循环的多次迭代中被锁定。(即游戏运行良好)

编辑:我在示例中添加了一些额外的东西,以防万一这是问题的原因。基本上,更新和绘制实体数组而不是单个实体

4

2 回答 2

2

我认为这里的问题可能是“同步”块不能保证公平。

OpenGL 线程将始终连续渲染,因此它会在完成后立即尝试重新进入 onDraw。由于选择允许哪个线程进入同步块是任意的,因此 OpenGL 线程可能会在将锁释放给物理线程之前尝试重新获取锁,并且基于一些任意标准,它会被锁定并且在不允许物理线程进入的情况下结束。

这可以解释为什么它有时会发生而不是其他时候,因为这是一个任意决定。

您可以尝试实现公平锁而不是同步块,或者使 OpenGL 自上次物理更新后不会多次尝试重绘场景(让渲染线程休眠,直到发生更新)。

于 2012-07-25T05:57:13.253 回答
0

最后,我选择了作弊解决方案。我将同步块放在对实体数组的访问周围,并将 for 循环放在带有 ArrayIndexOutOfBounds 的 try/catch 中:

void update(){
    try{
        for(int i = 0;i<entities.length;i++){               
            GameEntity entity = entities[i];                
            synchrnonized(entity){
                entity.updatePosition();
            }               
        }
    }catch(ArrayIndexOutOfBoundsException aioob){
        if(tryAgain){
            update();
        } else {
            return;
        }
    }
}

这个解决方案的问题是,如果从完全不相关的东西中entity.updateposition()抛出一个ArrayIndexOutOfBoundsException,那么我会抓住它并误解它。再加上整个事情有点混乱,每隔一段时间就会跳过一帧或更新

由于这似乎解决了问题,我怀疑最初的原因可能在于我的代码中更深层次的地方,在进入 for 循环和实际修改实体之间,我真的不认为在这里转储我的整个代码是公平的.

我会在几天内不回答这个问题,以防其他人有更好的解决方案

于 2012-07-26T15:23:50.827 回答