即使其他线程没有锁定,也需要等待同步对象

问题描述:

我正在开发一款Android游戏,并且有一个奇怪的问题,偶尔游戏会在弹回生命之前变得无响应很长一段时间。据我所知,这种停顿,如果它发生,只会在游戏启动时发生。一旦正常运行,游戏似乎表现自己。即使其他线程没有锁定,也需要等待同步对象

经过一番调查后,似乎onTouchEvent回调正在被阻止,试图获取它与游戏线程共享的同步对象的锁定。同时游戏线程正常运行,并且不会长时间保持对同步对象的锁定。

这是我的理解是,doTouchEvent中的同步块会在游戏线程释放该锁后立即获取mSyncObject上的锁。但在某些情况下,似乎游戏线程能够在doTouchEvent最终能够获取锁之前获得并释放数百次锁。

没有其他代码使用同一个对象进行同步。

我已经复制了以下代码的相关位。从我能收集到的信息来看,这并不是什么不寻常的事情,所以我对我所看到的奇怪行为感到有些莫名其妙。

希望对此有帮助。提前致谢!

class GameThread extends Thread { 

// ...some methods and members omitted... 

private volatile int mFrameCount = 0; 
private Object mSyncObject = new Object(); 

@Override 
public void run() { 
    while (!mShutDown) { 
     Canvas canvas = null; 
     long timestampA = System.currentTimeMillis(); 
     try { 
      synchronized (mSurfaceHolder) { 
       canvas = mSurfaceHolder.lockCanvas(null); 
       // Synchronized on our object... 
       synchronized (mSyncObject) { 
        long now = System.currentTimeMillis(); 
        if ((now > mLastTime) && !mPaused) { 
         double timestep = (double) (now - mLastTime)/1000.0; 
         mGame.update(timestep); 
        } 
        mLastTime = now; 
        if (canvas != null) { 
         mGame.draw(canvas); 
        } 
       } 
      } 
     } finally { 
      if (canvas != null) { 
       mSurfaceHolder.unlockCanvasAndPost(canvas); 
      } 
     } 

     // have tried inserting a sleep() here, but it didn’t help 

     ++mFrameCount; 
    } 
} 


// Called from the UI thread 
public boolean doTouchEvent(MotionEvent event) { 
    boolean result = false; 

    // Synchronized on our object... 
    // The game loop in run() acquires and releases a lock 
    // on this object on every frame, so would expect to be 
    // blocked here for no longer than one frame. 
    // However, on occasions have been blocked here for over 
    // 2000 iterations of the game loop. 
    int frameCount = mFrameCount; 
    synchronized (mSyncObject) { 
     int framesWaited = mFrameCount - frameCount; 
     if (framesWaited > 1) { 
      Log.i("Block", "doTouchEvent waited " + framesWaited + " frames for lock"); 
     } 

     if (!(mPaused || mShutDown)) { 
      result = mGame.doTouchEvent(event); 
     } 
    } 
    return result; 
} 

} 

看来这个问题是由于同步提供的公平性并不能保证等待线程获得锁的顺序。事实上,如果另一个cpu密集型线程反复获取并释放锁,则线程可以无限期地等待锁定。

看到这个主题讨论一模一样的问题......

http://groups.google.com/group/android-developers/browse_frm/thread/ffe76e4a433c8675/f424fb7dc3baeb10

...这里的一个线程安全的,但无同步解决方案的例子。

http://blog.tomgibara.com/post/208684592/avoiding-starvation

之前在上面的绊脚石,我改变了我的代码通过使用的ConcurrentLinkedQueue,这似乎也有效地消除了摊位线程之间的运动赛事。

..您正在为游戏循环的每个循环获取3个不同的锁!你必须想到一个更好的设计,你不需要锁。这会使您的游戏性能下降。

+0

起初,我已经在mSurfaceHolder上同步所有内容,但为doTouchEvent添加了单独的同步对象,以减少可能被阻止的数量(尽管不是太多)。 – threeshinyapples 2012-02-25 20:26:33

+0

对不起,提交得太快,无法编辑,因为太多的时间过去... 我意识到最好的解决方案将涉及到一个轻微的重新设计,以确保触摸事件可以安全地处理,而不依赖于同步。 但我真的很想知道为什么我看到这种情况,UI线程似乎无法获得它的同步锁定很长一段时间,即使游戏线程给了它大量的机会。 – threeshinyapples 2012-02-25 20:34:27