9

私には奇妙な問題があります-誰かが私に何が起こっているのか、そして可能な回避策を説明してくれることを望んでいます。別のスレッドでjava.util.Timerオブジェクトを使用して、JavaでZ80コアを実装し、速度を落とそうとしています。

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

上記の方法は、Windows 7(2台のマシンを試した)で完全に機能しますが、2台のWindows XPマシンも試しましたが、両方で、Timerオブジェクトが約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の時間を生成しますが、これはまったく問題ありません。ただし、同じjarをWindows XPで実行すると、約1562ms〜1566msの時間が発生します。これは、私がこれをテストした2台の別々のXPマシン上にあります。それらはすべてJava6update27を実行しています。

タイマーが20ms(非常に小さい値)スリープしているため、この問題が発生していることがわかります-1秒間すべての実行ループをwait wait()-notifyAll()サイクルにバングすると、正しい結果が生成されます-I '私がやろうとしていること(50fpsでセガマスターシステムをエミュレートする)を見る人は、これが解決策ではないことを理解するでしょう-それはインタラクティブな応答時間を与えず、50ごとに49をスキップします。私が言うように、Win7はこれにうまく対応します。私のコードが大きすぎる場合は申し訳ありません:-(

4

3 に答える 3

5

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

あなたが見ている問題はおそらくクロック解像度に関係しています。一部のオペレーティングシステム(Windows XP以前)は、寝坊し、待機/通知/スリープ(一般的に中断)が遅いことで有名です。一方、他のオペレーティングシステム(私が見たすべてのLinux)は、指定されたほぼ瞬間に制御を返すのに優れています。

回避策は?短時間の場合は、ライブ待機(ビジーループ)を使用します。長時間、あなたが本当に望むよりも短い時間寝て、それから生きて残りを待ちます。

于 2011-09-26T23:05:51.130 に答える
2

私はを忘れて、ビジーループをTimerTask使用します:

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

2ミリ秒の遅延により、ホストOSは他の作業に十分な時間をかけることができます(とにかくマルチコアを使用している可能性があります)。残りのプログラムコードははるかに単純です。

ハードコーディングされた2ミリ秒が鈍器である場合は、必要なスリープ時間を計算してThread.sleep(long, int)過負荷を使用できます。

于 2011-09-26T20:24:22.570 に答える
1

WindowsXPでタイマーの解像度を設定できます。

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などのアプリケーションを実行している場合は、タイマーの解像度が向上します。したがって、エミュレータの実行中に音楽を聴いていると、タイミングが良くなる可能性があります。

于 2012-03-22T14:31:09.243 に答える