Долгое ожидание на объекте синхронизации, даже если другой поток не удерживает блокировку - PullRequest
1 голос
/ 26 февраля 2012

Я занимаюсь разработкой игры для 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;
}

}

Ответы [ 2 ]

1 голос
/ 28 февраля 2012

Может показаться, что эта проблема связана с тем фактом, что синхронизация не гарантирует честности, когда речь идет о порядке, в котором ожидающие потоки получат блокировку.Фактически, поток можно ожидать бесконечно в ожидании блокировки, если другой поток, интенсивно использующий процессор, многократно получает и снимает блокировки.

См. Этот поток, обсуждающий точно такую ​​же проблему ...

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

... и вот пример поточно-безопасного, но без синхронизации решения.

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

Прежде чем наткнуться на вышесказанное, я изменилмой код для передачи событий движения между потоками с использованием ConcurrentLinkedQueue, что также показалось эффективным в устранении остановок.

0 голосов
/ 26 февраля 2012

.. Вы получаете 3 разных блокировки на каждом цикле вашего игрового цикла!Вы должны думать о лучшем дизайне, в котором вам не нужны замки.Это снизит производительность вашей игры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...