在与Android的同步GLThread在与、Android、GLThread

2023-09-06 15:58:43 作者:叼烟的女汉子

我在使用机器人的OpenGL-ES做一个游戏,使用多线程:

I'm making a game in android using opengl-es, using multiple threads:

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卡住等待被释放锁(即当我去调试和暂停线程,它只是坐在问题同步(entityLock)更新()

什么是真正奇怪的是,经过一段时间(2秒和一分钟之间),该PhysThread会畅通,游戏将继续没有任何线程被锁出比线环的几个迭代多。 (即游戏运行罚款)

What's really strange is that after a while (between 2 seconds and a minute), the PhysThread will be unblocked, and the game will continue without either thread getting locked out for more than a few iterations of the thread loops. (i.e. the game runs fine)

编辑:我增加了一些额外的东西的例子,只是这是问题的原因情况。基本上,更新和绘制实体的阵列,而不是一个单一的实体

I added some extra stuff to the example, just in case that is the cause of the problem. Basically, updating and drawing an array of entities rather than a single entity

推荐答案

在最后,我去作弊的解决方案。我把同步块左右进入实体阵列,把一个try / catch语句的ArrayIndexOutOfBounds带内循环:

In the end, I went for the cheat solution. I put the synchronization blocks around access to the entities array and put the for loops inside a try/catch with ArrayIndexOutOfBounds:

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异常从完全不相关的东西,然后,我会抓住它,并misinter preT它。再加上整个事情是有点乱,有一次在每一段时间,一帧或更新被跳过

The problem with this solution is that if entity.updateposition() throws an ArrayIndexOutOfBoundsException from something completely unrelated, then I'll catch it and misinterpret it. Plus the whole thing is a bit messy, and once in every while, a frame or update gets skipped

由于这似乎解决了这个问题,我怀疑最初的原因很可能在于我的code更深的地方,在进入for循环和实际修改实体之间,我真的不觉得这是公平的在这里倾倒我的整个code。

As this appears to solved the problem, I suspect the original cause probably lies somewhere deeper in my code, between entering the for loops and actually modifying the entities, and I don't really think it'd be fair to dump my entire code here.

我会离开的问题悬而未决的情况下,在任何一两天人有一个更好的解决方案。

I'll leave the question unanswered for a couple days in case anyone else has a better solution