2011-09-26 26 views
9

私は奇妙な問題があります - 誰かが私に何が起こっているのかを説明し、回避策を考えてもらいたいと思っています。私はJavaでZ80コアを実装しており、別のスレッドでjava.util.Timerオブジェクトを使って速度を落とそうとしています。Windows XPとWindows 7のJavaタイミングの精度

基本的なセットアップでは、実行ループを1つのスレッドで1秒間に50回実行しています。しかし、この実行ループ内では、多くのサイクルが実行され、wait()が呼び出されます。外部タイマースレッドは、P80 Sega Master Systemのクロック周波数3.54MHz(ish)をシミュレートして、Z80オブジェクトのnotifyAll()を20msごとに呼び出します。

上記の方法は、Windows 7(2台のマシンを試しました)では完全に動作しますが、2台のWindows XPマシンを試してみましたが、どちらもタイマーオブジェクトが約50%ほど遅くなっています。つまり、Windows XPマシンでは、1秒間のエミュレーション時間が実際には約1.5秒かかります。

私はTimerオブジェクトの代わりにThread.sleep()を使用しようとしましたが、これは全く同じ効果があります。私はほとんどのOSで1ミリ秒より良いとは言えないが、1000ミリ秒ではなく999ミリ秒または1001ミリ秒で時間を細分化できることを認識している。私が考えることができないことは1562msです - 私の方法は新しいバージョンのWindowsではうまくいくが、古いものではどうして理解できないのですか?私は割り込み期間などを調査しましたが、回避策を開発しました。

誰でもこの問題の原因と推奨される回避策を教えてください。どうもありがとう。

アップデート:ここでは、私は同じ問題を表示するために構築された小さなアプリケーションのための完全なコードです:

import java.util.Timer; 
import java.util.TimerTask; 

public class WorkThread extends Thread 
{ 
    private Timer timerThread; 
    private WakeUpTask timerTask; 

    public WorkThread() 
    { 
     timerThread = new Timer(); 
     timerTask = new WakeUpTask(this); 
    } 

    public void run() 
    { 
     timerThread.schedule(timerTask, 0, 20); 
     while (true) 
     { 
     long startTime = System.nanoTime(); 
     for (int i = 0; i < 50; i++) 
     { 
      int a = 1 + 1; 
      goToSleep(); 
     } 
     long timeTaken = (System.nanoTime() - startTime)/1000000; 
     System.out.println("Time taken this loop: " + timeTaken + " milliseconds"); 
     } 
    } 

    synchronized public void goToSleep() 
    { 
     try 
     { 
     wait(); 
     } 
     catch (InterruptedException e) 
     { 
     System.exit(0); 
     } 
    } 

    synchronized public void wakeUp() 
    { 
     notifyAll(); 
    } 

    private class WakeUpTask extends TimerTask 
    { 
     private WorkThread w; 

     public WakeUpTask(WorkThread t) 
     { 
      w = t; 
     } 

     public void run() 
     { 
      w.wakeUp(); 
     } 
    } 
} 

すべての主要なクラスはありませんこれらのワーカースレッドの1つを作成して開始されます。 Windows 7では、このコードは約999ms〜1000msの時間を生成しますが、これはまったく問題ありません。 Windows XPで同じjarファイルを実行すると、約1562ms〜1566msの時間が発生します。これは、これをテストした2台の別のXPマシンにあります。彼らはすべてJava 6のアップデート27を実行しています。

タイマーが20ms(かなり小さな値)のためにスリープしているので、この問題が発生しています - すべての実行ループを1秒間待機してwait wait() notifyAll()サイクルでは正しい結果が得られます - 私がしようとしていることを見ている人は(50fpsでSega Master Systemをエミュレートします)、これは解決策ではないことがわかります。インタラクティブな応答時間は、50のうち49をスキップします。私が言うように、Win7はこれにうまく対応します。私のコードが大きすぎる:-(

+0

興味深い質問です。この動作を示す自己完結型のコードスニペットを提供することは可能でしょうか? – NPE

+0

もしあなたが好きなら私はあなたにタイマーのソースコードを与えることができますか?私はあなたに多くを与えたいと思いますが、Z80コアは非常に重くなっています;-) – PhilPotter1987

+1

@aix * "自己完結型コードスニペット" *自己完結型コードは短いかもしれませんが、定義上、スニペット](http://en.wikipedia.org/wiki/Snippet_%28programming%29)。私は[SSCCE](http://pscode.org/sscce.html)を投稿することをお勧めします。 –

答えて

5

誰でもこの問題の原因と推奨される回避策を教えてください。

おそらく表示されている問題は、clock resolutionと関連があります。いくつかのオペレーティングシステム(Windows XPおよびそれ以前)は、待ち時間/通知/スリープ(一般的な割り込み)で遅くなることや悪いことで有名です。一方、他のオペレーティングシステム(私が見てきたすべてのLinux)は、指定された瞬間に制御を戻すことに優れています。

回避策はありますか?短期間は、ライブ待機(ビジー・ループ)を使用します。長い間、あなたが本当に望む時間より短い時間で眠り、残りの部分を待ってください。

+1

ループを使用する場合は、[System.nanoTime()](http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime())を使用します。 currentTimeMillis()はThreadと同じ細分性を持つため、[System.currentTimeMillis()](http://download.oracle.com/javase/6/docs/api/java/lang/System.html#currentTimeMillis())よりも優先されます。睡眠()。 [リンク](http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime)を参照してください。 – prunge

+0

XPでは時計の解像度を1msに設定する方法はありませんか?私はそこにある印象を受けていましたが、明らかに私がそれをすることができなかったので私は間違っているかもしれません;-) – PhilPotter1987

+0

申し訳ありません。それはOSのことです。 Javaプログラムが誤ってプラットフォーム固有のものになる可能性のある隠された方法の1つ。 –

2

であれば申し訳ありませんが、私はTimerTaskを見送るだけビジーループ使用したい:2ミリ秒の遅延が他のもので動作するように時間のホストOSの多くを与え

long sleepUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(20); 
while (System.nanoTime() < sleepUntil) { 
    Thread.sleep(2); // catch of InterruptedException left out for brevity 
} 

ハードコードされた2ミリ秒があまりにも多くの鈍器である場合は、必要なスリープ時間を計算することができます。また、必要なスリープ時間を計算することもできます。 Thread.sleep(long, int)の過負荷を使用してください。

+0

これをありがとうBarend - 私はこれを考慮しなかった。私はそれを試して、それは私の要求された時間範囲にはるかに近いです - まだ十分ではありません。必要なスリープ時間を計算して、Thread.sleep(long、int)を使用しても平均で約90msの余裕があります。私が言ったように、私は1〜2ミリ秒ほど離れていても構わないが、これはあまりにも多い。私は仕事から家に帰ってWindows 7でこれを試してみると、うまくいくことは間違いありません。なぜこの問題が存在するのかまだ答えられていません。 – PhilPotter1987

1

You can Windows XPでタイマーの解像度を設定します。

http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624%28v=vs.85%29.aspx

これはシステム全体の設定ですので、あなたはこれがあなたの問題であるかどうかを確認することができるように解像度を設定するためのツールを使用することができます。

はこれを試してみて、それが助けかどうかを確認:http://www.lucashale.com/timer-resolution/

をデフォルトでは、新しいバージョンが緊密なタイミングがあるかもしれない、ので、あなたは、Windowsの新しいバージョンでよりよいタイミングが表示されることがあります。また、Windows Media Playerなどのアプリケーションを実行している場合は、タイマーの解像度が向上します。エミュレータを実行しているときに音楽を聴いていると、素晴らしいタイミングが得られる可能性があります。

+0

ありがとうございます - 私のエミュレータは特にクロスプラットフォームになるように設計されていますので、このようなWindowsの仕様に頼ることはできません。最後に、スリープスレッドを使用しました。コードは、タイミングが20msごとに1ms以上出ると、ビジーな待機ループに戻ります。今はXP上でかなりうまくいくと思われます。 – PhilPotter1987

+0

Phil - プラットフォームごとに特別なケースコードを書くことなく、優れたマルチプラットフォームソリューションのように思えます。ニース – dss539

関連する問題