libgdx - 在其他线程中做某事不起作用

时间:2014-03-15 12:58:26

标签: java multithreading asynchronous libgdx

我的游戏有一个统计队列,每场比赛后当前的游戏统计数据进入队列。 每当主菜单开始时我想将所有游戏统计数据上传到服务器,这需要1-3秒,我想在另一个线程中执行此操作。

我的代码

    @Override
    public void show() {
        Global.key = Global.getKey();
        // System.out.println(Stats.getJSONObject(Global.key));

        Gdx.app.postRunnable(new Runnable() {
            @Override
            public void run() {

                Stats.TryUploadGame1();
                System.out.println("DONE");
            }
        });
.....

}

但这也冻结了我的游戏。

我该怎么办?

3 个答案:

答案 0 :(得分:4)

您当前的代码正在发布Runnable实例,该实例将在下一帧之前由渲染线程执行。通常使用Gdx.app.postRunnable API,因此后台线程可以要求在渲染线程上发生某些事情。您希望发布Runnable以执行除渲染线程以外的任何位置。

只要你的统计代码根本不与OpenGL上下文交互(因为Android OpenGL API假设只有一个线程与它们交互),你可以在新的后台线程上发布你的Runnable: / p>

new Thread(new Runnable() { ... }).start();

这应取消阻止你的渲染。 (当然,如果你的后台线程使用了大量的CPU,它仍然会干扰渲染线程,但如果它主要阻塞IO或主机有备用内核,它就不应该干扰。)

这可以通过多种方式进行改进(使用ThreadPool,或使用Android感知后台任务支持),但如果您的统计信息更新速度相对较快且线程创建频繁,则应该可以正常工作。

答案 1 :(得分:3)

好的,在其他线程中做一些事情,你需要处理 OpenGL上下文。在一个不同的线程内,你不能做任何渲染东西的事情。你被迫以任何方式将这种东西推入渲染中。你需要synchronize可以从libgdx的常规渲染线程中调用的所有东西。例如,你想从一个不同线程的舞台调用.act(float delta),你被迫将舞台indo放在synchronized块中。

可运行的帖子不是一个帖子。它是一个可运行的,在下一个rendercall的开头执行。因此它将停止游戏,直到它完成但它在OpenGl上下文线程内。 (这就是你的游戏停止的原因)

以下是如何在libgdx中使用线程的示例。我在游戏中使用它。它运行210帧,每秒210次更新调用。您可以将其更改为尽可能快或只需60fps即可:

public class GameLogicThread extends Thread {

    private GameScreen m_screen;
    private boolean m_runing;
    private long m_timeBegin;
    private long m_timeDiff;
    private long m_sleepTime;
    private final static float FRAMERATE = 210f;

    public GameLogicThread(GameScreen screen) { //pass the game screen to it.
        m_screen = screen;
        setName("GameLogic");
    }

    @Override
    public void run() {
        m_runing = true;
        Logger.log("Started");
        while (m_runing) {
            m_timeBegin = TimeUtils.millis();
            // act of the camera
            synchronized (m_screen.figureStage) { //stage with figures
                // now figures
                if (m_screen.m_status == GameStatus.GAME) {
                    m_screen.figureStage.act(1f / GameLogicThread.FRAMERATE);
                }
            }
            m_timeDiff = TimeUtils.millis() - m_timeBegin;
            m_sleepTime = (long) (1f / GameLogicThread.FRAMERATE * 1000f - m_timeDiff);
            if (m_sleepTime > 0) {
                try {
                    Thread.sleep(m_sleepTime);
                }
                catch (InterruptedException e) {
                    Logger.error("Couldn't sleep " + e.getStackTrace());
                }
            } else {
                Logger.error("we are to slow! " + m_sleepTime); //meight create it dynamic so if you are to slow decrease the framerate till you are not to slow anymore
            }
        }
    }

    /**
     * Stops the thread save<br>
        */
    public void stopThread() {
        m_runing = false;
        boolean retry = true;
        while (retry) {
            try {
                this.join();
                retry = false;
            }
            catch (Exception e) {
                Logger.error(e.getMessage());
            }
        }
    }
}

这确实更新了我的所有数据。为了不使渲染线程出现任何问题,使得图形台同步。 (关键部分的种类)

不要忘记每次停止时都需要创建一个新线程。所以例如在节目内你需要这个:

@Override
public void show() {
    super.show();
    m_logic = new GameLogicThread(this); //create a new one inside of the GameScreen
    m_logic.start(); //start the thread
}

也不要忘记在暂停站内等待它。等等。

@Override
public void dispose() {
    m_logic.stopThread();
}

答案 2 :(得分:1)

根据wiki

  

要从另一个线程将数据传递到呈现线程,我们建议使用Application.postRunnable()。这将在调用ApplicationListener.render()之前在下一帧的渲染线程中运行Runnable中的代码。

因此,调用该方法只是创建一个新的线程,以便在渲染线程上运行。

你可能想在创建线程时使用标准的java实践,除非在libgdx中因为android而不赞成,我不确定。