我正在使用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)
真正奇怪的是,经过一段时间(2秒到1分钟之间),PhysThread将被解锁,并且游戏将继续,而不会有任何线程被锁定超过线程循环的几次迭代。 (即游戏运行良好)
编辑:我在示例中添加了一些额外的内容,以防万一是导致问题的原因。基本上,更新和绘制实体数组而不是单个实体
答案 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循环和实际修改实体之间,我真的不认为转储我的公平整个代码在这里。
如果其他人有更好的解决方案,我会在几天内将问题留答无法解答