2

私はちょうどそれ自身のスレッドでこれを行うために使用されるプログラムを持っています:

public void run(){
    long lastTime = System.nanoTime();
    float lastSleep = 0;
    //Everything is in seconds.
    while(running){
        float delta = (System.nanoTime()-lastTime)/1000000000f;
        lastTime = System.nanoTime();
        manager.update(delta);
        panel.repaint();
        lastSleep = Math.max(maxTicSpeed-(delta-lastSleep),5/1000f);
        try{
            Thread.sleep((long) Math.round(lastSleep*1000));
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

基本的に、このようにループするときは常にスリープするように教えられていたので、プログラムは少なくとも 5 ミリ秒、または制限を超えずにスリープできる最大時間 (1/30 秒) スリープします。しかし、私は読み回していましたが、睡眠はあまり良いようには聞こえません.

http://msmvps.com/blogs/peterrichie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

彼の言うことからすると、私のプログラムは、スリープの下限に近づきすぎるとスリープすらしないように思えます。時間の変化を system.printing すると、変化はおよそ .31508 から .03475 の範囲になります。これは、私のプログラムが不正確さを説明しているため、私にとっては十分な値です。

そうは言っても、代わりに何ができますか?try{Sleep} の代わりに次のようなものを追加することを検討していました。

long waitTill = (long) (System.nanoTime()+lastTime/1000000000f), 
    now = System.nanoTime();
while(now < waitTill){
    now = System.nanoTime();
}

しかし、私のスレッドは依然として同じ量のプロセッサー時間を占めているのではないでしょうか? 要点は、スレッドが実際に必要以上にプロセッサを占有しないようにすることだと思いました..

それで、スリープを使用する必要がありますか (最小スリープ時間を長くしますか?)、代替手段を使用する必要がありますか、別の代替手段を使用する必要がありますか? 睡眠の不正確さを考慮しても、プログラミングが不十分ですか?

助けてくれてありがとう!

編集: タイマーが推奨されていますが、タイマーが再度呼び出される前にタスクが終了しなかった場合、問題が発生することを理解しています。それは私のプログラムにとって明確な懸念事項です。デルタを使用して Thread.sleep() の問題に対処したような気がするので、以前の Thread.sleep() の方が良いでしょうか?

4

5 に答える 5

5

この問題を回避するには、設計を再考する必要があります。基本的に、あなたがしていることは、定期的にスケジュールされた間隔で作業を行うことです。Run/While/Sleep パターンは機能しますが、研究で明らかになったように、これは最適なソリューションではありません。

現代の言語には、プログラミング環境/OS がタスクの実行をより適切に管理できるようにする「タスク実行」パターンがあります。

Java には、 java.util.timerとともにjava.util.timertaskがあります。タスク実行パターンでは、タスクを作成し、特定の間隔で実行するようにスケジュールします。

タイマーは、ブール値フラグを設定する代わりにタイマーをキャンセルすることで、実行中のループを停止するためのよりクリーンな方法も提供します。

コメントから:

知っておくべき問題がいくつかあります。タスクがスケジュールされた間隔よりも長く実行される可能性があるタスクである場合、前のタスクがまだ実行されている間に別のタスクを実行することができます。いくつかの解決策:

  1. 実行する作業をキューに入れるには、ジョブ キューを使用します。キューに作業がない場合、タスクは戻ります。
  2. JavaScript で一般的なもう 1 つの方法は、タスクを 1 回実行するようにスケジュールし、タスクの最後に、そのタスクを 1 回実行するように再スケジュールすることです。
  3. 特定のタスクが実行されていることを示すためにフラグを使用する、洗練されていないアプローチ。これは機能しますが、エラーが発生しやすいフラグの状態を適切に管理する必要があります。

もう 1 つの一般的な問題は、スケジュールされたタイマーがベストエフォートとして実装されることが多いことです。つまり、OS/フレームワークはスケジュールどおりにタスクを実行しようとしますが、タスクが指定された間隔で正確に実行されるという保証はありません。したがって、タスクにハードで決定論的なスケジューリングが必要な場合は、OS/ハードウェアに近いソリューションが必要になる可能性があります。

于 2013-06-23T23:17:28.037 に答える
1

純粋なスピン(2 番目の例)を絶対に避ける必要があります (同期プリミティブのような特定のユースケースを除く)。これは、CPUを待機するだけで消費し、簡単にデッドロックにつながる可能性があるためです。

これが、CPU の消費を減らし、他のスレッドが動作する機会を増やして、デッドロックのリスクを軽減するために、スリープで遅延を使用することがよくある理由です。

したがって、自分が何をしているのかを知っている限り、最初のループに問題はありません。

ただし、これらの種類のユースケース専用のコンポーネントを優先する必要があります: timers

于 2013-06-23T23:17:42.440 に答える
0

sleep() の代わりに、wait() と notify() を使用することをお勧めします。

sleep() を使用すると、スレッドは実行中のキューから出て、スレッドを再度実行するために、OS は純粋なオーバーヘッドである余分な作業を行う必要があります。

ただし、スレッドが待機している場合、実行中のキューから出ないため、オーバーヘッドはありません。

于 2013-06-24T07:33:22.447 に答える