2008-12-12 43 views
6

程序严格循环不好吗?严格循环不好?

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

但是,我最近没有去调整updateGame线程,并且模拟我的对象移动似乎更加现实,因为我已经取出了10ms的睡眠时间。热循环不好还是有紧缩循环?

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; 
     } 
    } 
} 

在这个例子中,我只是睡了1毫秒(从我的理解有多么毫秒准确性和休眠功能的工作原理这可能更像是5-10ms。如果我睡任何超过这个启动有撞击我的碰撞检测和物理模型的准确性。

它是一个不好的做法,有紧密的循环,或在他们1ms的休眠循环?有别的我应该做的呢?

+1

你应该使用'的Thread.sleep(...)的``而不是Thread.currentThread()。睡眠(。 ..)`因为这是一个静态方法;你的代码可能会诱使你执行`someOtherThread.sleep(...)`,它不会睡眠`someOtherThread`。 – newacct 2009-09-19 04:40:48

回答

9

我读一个真正伟大的一篇关于切实有效地执行物理运算的循环:Fix Your Timestep!

当游戏运行时,通常是用户关心这么紧循环的主要应用是不是什么大不了的事。尽管安排您的更新,您应该做什么。你应该知道多久 - 以你的目标帧速率 - 你的帧必须执行。你应该测量帧的时间,并且只在你的帧减去已知帧时间的时候睡觉。这样,您的系统将锁定到帧速率,而不会随着帧的渲染时间而变化。

另一件事是我不相信线程。睡眠具有非常好的分辨率,大大超过5毫秒,您可能需要寻找更准确的Java定时器。

+0

我有两个线程,我的updateGame线程和我的渲染线程。我的渲染线程被锁定到一个maxFrameRate,正如你所说的基于它的经过时间。 – mmcdole 2008-12-12 03:28:44

2

这只是如果它对系统中的某些东西有不利影响,则为“坏”,而不是睡1ms,而不是在条件为wa更新版本,最少1ms。这样,你总是会睡1ms以上,如果没有什么可做的话会更长。

2

正如Adam在他的回答中指出的那样,可能会对系统的性能产生不利影响。

我也尝试过以非常相似的方式制作游戏(在单独的线程上进行渲染和运动计算),我发现没有Thread.sleep会导致Java应用程序占用CPU的很大一部分时间。

要考虑的另一件事是系统计时器本身。正如你所提到的,尽管Thread.sleep方法需要几毫秒的休眠时间,但是这个精度取决于操作系统提供的定时器(如API规范中所述)。在基于Windows NT的操作系统的情况下,timer resolution is 10 milliseconds。 (另请参阅:System.currentTimeMillis vs System.nanoTime

确实,Thread.sleep有可能会降低应用程序的性能,但不会导致应用程序的系统利用率大幅上升。

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

0

对于一个游戏,可能不是。只要确保你的游戏在交换机任务时暂停。

0

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

1

另外考虑笔记本电脑用户,不断运行一个紧密的循环将保持CPU运行的艰难,这将通过他们的电池咀嚼(许多Flash游戏是有罪的)。在决定是否节制循环时要考虑一些事情。

1

joshperry的答案几乎是你想要的,但也有几个方法。如果您使用多个线程,则还必须处理锁定等问题。具体取决于您的游戏体系结构,这可能是/不是什么大问题。例如,你是否做了大量的锁定,线程之间是否有大量的消息传递等等。如果你是一个传统的游戏,你通常会有一个主循环 - 我有一个CMD对象队列(如果你喜欢,可以运行,但可以也就是更多类似于自然界的事件总线),它们会连续执行直到队列为空。线程然后等待,直到它发出新的cmd在队列中的信号。对于大多数游戏来说,这通常就够了那么问题就变成了如何/何时添加cmds。我使用一个定时器/调度器(也注意关于java时间分辨率的注释)以所需的帧速率向主循环添加一个cmd。这样做的好处在于对笔记本电脑也很友善。在启动时,您还可以对系统进行基准测试,以查看系统运行速度,然后设置适当的帧速率(即从支持的基础开始,然后工作到最大)。基准测试或使用用户指定的性能提示(即渲染细节数量)可以由每种类型的cmd使用(即,渲染scence cmd/event会查看性能设置的详细信息等)。 (注意 - cmds不必是可运行的,它们可以更像是一个带有在主线程上调用的侦听器的事件总线)。如果一个任务想要使用多线程/内核的cmd处理程序(如果它的事件类型模型 - 我个人喜欢事件模型 - 它更容易访问共享状态信息而不需要全局单例)然后可以产生多个任务(比如使用现有的线程池 - 所以每个cmd都不会触及新线程的开销),然后使用屏障类型类等待所有任务完成。这种方法通常会使锁定更容易,因为每个cmd(或系统)通常具有不同的锁定要求。因此,您可以只实现该系统的锁定,而不必担心子系统之间的锁定 - 即。对于物理学来说,你可以锁定游戏区域中的对象束,并且线程池中的每个分支线程都只会担心它的对象,即。 thread1处理objects1到20,thread2对象21-40等(这只是为了说明每个cmd如何实现自定义锁定算法的概念,该算法最适合它正在做什么,而不必担心其他子系统在做什么与共享状态数据)。

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