在Android中与GLThread同步

时间:2012-07-24 21:39:51

标签: java android multithreading opengl-es synchronized

我正在使用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)

内{1}}

真正奇怪的是,经过一段时间(2秒到1分钟之间),PhysThread将被解锁,并且游戏将继续,而不会有任何线程被锁定超过线程循环的几次迭代。 (即游戏运行良好)

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

2 个答案:

答案 0 :(得分:2)

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

OpenGL线程将始终连续呈现,因此它将在完成后立即尝试重新输入onDraw。由于允许哪个线程进入同步块的选择是任意的,因此OpenGL线程可能会在将锁释放到物理线程之前尝试重新获取锁,并且基于某些任意条件,它会被锁定。并且不允许物理线程进入。

这可以解释为什么它有时发生而不是其他发生,因为这是一个武断的决定。

您可能尝试实现公平锁定而不是同步块,或者使得OpenGL不会尝试自上次物理更新后多次重绘场景(将渲染线程置于睡眠状态直到更新发生)。

答案 1 :(得分: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循环和实际修改实体之间,我真的不认为转储我的公平整个代码在这里。

如果其他人有更好的解决方案,我会在几天内将问题留答无法解答