2008-12-12 44 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如何實現自定義鎖定算法的概念,該算法最適合它正在做什麼,而不必擔心其他子系統在做什麼與共享狀態數據)。

重要的是看你如何以及爲什麼使用線程和鎖定等