紧密循环不好吗?

时间:2008-12-12 03:06:56

标签: java

程序中的紧密循环是不是很糟糕?

我有一个应用程序,它有两个用于游戏物理模拟器的线程。 updateGame线程和渲染线程。通过使线程休眠几毫秒(以实现我想要的帧速率)来限制渲染线程,并且updateGame线程(基于一些物理方程更新我的游戏对象位置)先前被10毫秒的睡眠限制

然而,我最近没有对updateGame线程进行调整,并且我的对象运动的模拟似乎更加逼真,因为我已经取出了10毫秒的睡眠时间。热循环或紧密循环是不是很糟糕?

private class UpdateTask implements Runnable
{
    private long previousTime = System.currentTimeMillis();
    private long currentTime = previousTime;
    private long elapsedTime;


    public void run()
    {
        while(true)
        {
        currentTime = System.currentTimeMillis();
        elapsedTime = (currentTime - previousTime); // elapsed time in seconds


        updateGame(elapsedTime / 1000f);

            try {
                Thread.currentThread().sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        previousTime = currentTime;
        }
    }
}

在这个例子中,我只是睡了1ms(根据我对毫秒精度和睡眠功能如何工作的理解,这可能更像是5-10ms。如果我睡不着这个,它开始对它产生影响我的碰撞检测和物理模型的准确性。

在其中睡眠时间为1ms的紧密循环或循环是不是一种不好的做法?还有什么我应该做的吗?

7 个答案:

答案 0 :(得分:9)

我读了一篇关于高效且有效地执行物理计算循环的帖子:Fix Your Timestep!

当游戏运行时,通常是用户关心的主要应用程序,因此紧密循环并不是一件大事。你应该做什么,但安排你的更新。您应该知道 - 在目标帧速率下 - 您的帧必须执行多长时间。您应该测量帧占用的时间,并且仅在帧占用的时间减去已知的帧时间后休眠。这样,您的系统将锁定帧速率,而不会随着帧渲染所需的时间而变化。

另一件事是我不相信Thread.sleep具有非常好的分辨率,超过5毫秒,你可能想要寻找一个更准确的Java定时器。

答案 1 :(得分:2)

如果它对您系统中的其他内容产生负面影响,那只是“不好”。您可能会在保证更新的情况下阻止,而不是睡眠1毫秒,至少1毫秒。这样你就可以一直睡到至少1毫秒,如果无事可做则更长。

答案 2 :(得分:2)

正如亚当在答案中指出的那样,可能会对系统的性能产生负面影响。

我也试过以非常类似的方式制作游戏(在不同的线程上进行渲染和运动计算),我发现没有Thread.sleep将导致Java应用程序占用非常重要的部分CPU时间。

要考虑的另一件事是系统计时器本身。正如您所提到的,尽管Thread.sleep方法占用了睡眠的毫秒数,但该精度依赖于操作系统提供的计时器(如API规范中所述)。对于基于Windows NT的操作系统,timer resolution is 10 milliseconds。 (另见:System.currentTimeMillis vs System.nanoTime

是的,让Thread.sleep有可能降低应用程序的性能,但是没有这样做会导致应用程序的系统利用率急剧上升。

我猜这个决定取决于应用程序是否应该占用系统利用率的很大一部分,或者表现得很好并与系统上运行的其他应用程序共享CPU时间。

答案 3 :(得分:1)

另外考虑一下笔记本电脑用户,连续运行紧密循环会让CPU保持运转困难,这会扼杀他们的电池(许多Flash游戏都是这样)。在决定是否限制循环时需要考虑的事项。

答案 4 :(得分:1)

joshperry的答案几乎是你想要的,但也有一些方法。如果您使用多个线程,您还必须处理锁定等。取决于您的游戏架构可能/可能不是什么大问题。例如,你做了很多锁定,线程之间是否有很多消息传递等等。如果你是传统游戏,你通常只有一个主循环 - 我有一个CMD对象的队列(如果你愿意,可以运行,但是可以也是更多的事件总线,就像在自然中一样,不断执行,直到队列为空。然后线程等待,直到发出新的cmd在队列中的信号。对于大多数游戏来说,这通常已足够那么问题就变成了如何/何时添加了cmds。我使用计时器/调度程序(还记下有关java时间分辨率的注释)以所需的帧速率将cmd添加到主循环。这样做的好处是对笔记本电脑也很友好。在启动时,您还可以对系统进行基准测试以查看其运行速度,然后设置适当的帧速率(即从支持的基础开始,然后工作到最大)。然后,每种类型的cmd可以使用基准或使用用户指定的性能提示(即,渲染细节的数量)(即,渲染场景cmd /事件查看性能设置以获得细节等)。 (注意 - cmds不必是可运行的,它们可能更像是一个带有在主线程上调用的侦听器的事件总线。)

此外,如果一个任务想要使用多线程/核心的cmd处理程序(如果它是一个事件类型模型 - 我个人喜欢事件模型 - 它更容易访问共享状态信息而不需要全局单例)可以然后产生多个任务(比如使用现有的线程池 - 因此每个cmd不会命中新线程的成本),然后使用barrier类型等待所有任务完成。此方法通常使锁定更容易,因为每个cmd(或系统)通常具有不同的锁定要求。因此,您只需实现该系统的锁定,而不必担心子系统之间的锁定 - 即。对于物理学,你可以锁定游戏区域中的对象束,然后线程池中的每个分叉线程只会担心它的对象,即。 thread1处理对象1到20,thread2对象21-40等(这只是为了说明每个cmd如何实现自定义锁定算法的概念,该算法最适合它正在做的事情,而不必担心其他子系统正在做什么共享状态数据)。

重要的是要看看你如何以及为什么使用线程和锁定等。

答案 5 :(得分:0)

对于游戏,可能不是。只需确保您的游戏在切换任务时暂停。

答案 6 :(得分:0)

在这种情况下,您实际上想要使用Thread.yield()。一个线程可能会连续运行,并且不允许任何其他线程执行时间。在每次迭代结束时放置一个yield调用会给调度程序一个提示,即是时候允许其他线程运行了。