9

Thread.Sleep()の解像度は1から15.6msまで変化します

このコンソールアプリを考えると:

class Program
{
    static void Main()
    {
        int outer = 100;
        int inner = 100;
        Stopwatch sw = new Stopwatch();

        for (int j = 0; j < outer; j++)
        {
            int i;
            sw.Restart();
            for (i = 0; i < inner; i++)
                Thread.Sleep(1);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

出力は100に近い100の数値になると予想しました。代わりに、次のようになります。

99 99 99100 99 99 99106106 99 99 99100100 99 99 99 99101 99 99 99 99 99101 99 99 99 99101 99 99 99100 99 99 99 99 99103 99 99 99 99 100 99 99 99 99 813 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1560 1559 1559 1559 1559 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1559 1558 1558 1558

しかし、正確な結果が得られない場合があります。毎回〜1559になります。

なぜ一貫していないのですか?

いくつかのグーグルは、15.6msがタイムスライスの長さであることを私に教えてくれたので、それは〜1559の結果を説明しています。しかし、なぜ私は時々正しい結果を得るのですか、そして他の時には私はちょうど15.6の倍数を得るのですか?(たとえば、Thread.Sleep(20)は通常〜31.2msを与えます)

ハードウェアまたはソフトウェアによってどのように影響を受けますか?

私がそれを発見した理由のためにこれを尋ねます:

私は32ビットのデュアルコアマシンでアプリケーションを開発していました。今日、私のマシンは完全なOSの再インストールで64ビットクアッドコアにアップグレードされました。(どちらの場合もWindows7と.NET4ですが、古いマシンにW7 SP1が搭載されているかどうかはわかりませんが、新しいマシンには搭載されています。)

新しいマシンでアプリケーションを実行すると、フォームがフェードアウトするのに時間がかかることにすぐに気付きます。10から50までの値でThread.Sleep()を使用する、フォームをフェードするカスタムメソッドがあります。古いシステムでは、これは毎回完全に機能するように見えました。新しいシステムでは、フェードするのに必要以上に時間がかかります。

この動作が古いシステムと新しいシステムの間で変わったのはなぜですか?これはハードウェアに関連していますか、それともソフトウェアに関連していますか?

一貫して正確にすることはできますか?(〜1msの解像度)

Thread.Sleep()を約1msまで確実に正確にするために、プログラムでできることはありますか?それとも10ms?

4

5 に答える 5

7

答えは、使用するのではなくThread.Sleep、代わりに高分解能タイマーを使用することです。忙しいループでフェードを行う必要がありますが、問題ないようです。高解像度を期待することはできず、Thread.Sleepハードウェアによって動作が異なることで有名です。

Stopwatchハードウェアでサポートされている場合は、高解像度のパフォーマンス カウンターを使用する .netのクラスを使用できます。

于 2011-09-30T19:09:38.097 に答える
7

「Sleep 関数は、少なくとも指定された間隔で現在のスレッドの実行を中断します。」</p>

-> http://social.msdn.microsoft.com/Forums/en/clr/thread/facc2b57-9a27-4049-bb32-ef093fbf4c29

于 2011-09-30T19:11:21.523 に答える
3

timeBeginPeriod() と timeEndPeriod() を呼び出すプログラムがマシン上で実行されています。通常、timeSetEvent() を使用して 1 ミリ秒のタイマーを設定するメディア関連のプログラムです。Sleep() の解決にも影響します。

これらの関数を自分でピンボークして、一貫した動作を得ることができます。ただし、UI 効果としてはあまり合理的ではありません。ラップトップのバッテリー寿命にはかなり不向きです。

20 ミリ秒スリープし、実際に 2/64 秒を取得することは、それ以外の場合は論理的です。CPU は、20 ミリ秒が経過したことに気付くほどすぐには起動しません。1/64 秒の倍数しか得られません。したがって、フェード効果を実装するタイマーの妥当な選択は 15 ミリ秒であり、64 fps アニメーションの最悪のケースが得られます。効果が十分に速く描画されると仮定します。timeBeginPeriod が呼び出された場合は少しずれますが、それほどではありません。時計からアニメーション ステージを計算することもできますが、私の本では少しやり過ぎです。

于 2011-09-30T20:49:06.070 に答える
3

私の質問の 1 つに答えることができます。一貫して正確にすることはできますか? (~1ms 分解能)

はい、timeBeginPeriod()と timeEndPeriod() を使用して、できるようです。

私はこれをテストしましたが、動作します。

私が読んだいくつかのことは、アプリケーションの期間中に timeBeginPeriod(1) を呼び出すことは悪い考えであることを示唆しています。ただし、短いメソッドの開始時に呼び出して、メソッドの最後に timeEndPeriod() でクリアすることは問題ありません。

それにもかかわらず、タイマーを使用して調査することもできます。

于 2011-09-30T19:40:30.320 に答える
-2

あなたのアプローチは間違っています。睡眠が正確になることは決してありません。また、マシンがビジーであるほど、睡眠ループが間違ったものになります。

あなたがすべきことは、経過した時間を見て、それに応じて適応することです. Sleep をぐるぐる回すのは悪い考えですが、それを行うための "より良い" 方法は、はるかに複雑になります。提案されたソリューションをシンプルに保ちます。

  • DateTime.Now.Ticks を呼び出して、変数 (startTick) に保存します。
  • ループ内で、既に行っているように Sleep を呼び出します。
  • DateTime.Now.Ticks を再度呼び出し、そこから startTick を減算します。この値は、フェードを開始してから 100 ナノ秒単位で経過した時間 (timeSinceStart) になります。
  • timeSinceStart を使用して、経過した時間でどれだけフェードする必要があるかを計算します。
  • 完全に消えるまで繰り返します。
于 2011-09-30T19:52:18.377 に答える