20

経過時間が予想どおりではないため、断続的に失敗する単体テストに遭遇しました。

このテストの例は次のとおりです。

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

TimeSpan oneSecond = new TimeSpan(0, 0, 1);

for(int i=0; i<3; i++)
{
    Thread.Sleep(oneSecond);
}

stopwatch.Stop();

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, 2999);

ほとんどの場合、これは通過しますが、少なくとも1回は失敗しました。理由は、次のとおりです。

期待:2999以上しかしだった:2998

どうして3秒もかからないのかわかりません。Thread.SleepまたはおそらくStopwatchに、私が気付いていない精度の問題がありますか?

以下の質問のいくつかの更新と同じように。単体テストされているシナリオは、メソッドを呼び出して何らかのアクションを実行できるようにするクラスであり、失敗した場合は1秒待って、そのメソッドを呼び出します。上に示したテストは、起こっていることの単なる概算です。

DoSomething()メソッドを呼び出したいとしましょう...しかし、DoSomething()によって例外がスローされた場合、最大3回まで呼び出しを再試行できるようにしたいのですが、各試行の間に1秒待ちます。この場合の単体テストの目的は、3回の再試行を要求したときに、各再試行の間に1秒の待機があり、合計時間が3秒を超えていることを確認することです。

4

6 に答える 6

19

あなたのスレッドは他のスレッドとCPU時間を共有しています。スリープは、あなたの番になり、カーネルがスリープ時間が経過したことを認識するとすぐに終了するため、それほど正確ではありません。

CPU負荷、プロセスの優先順位、同時スレッドの数は、他のプロセスからのものであっても、それに影響します。

于 2009-08-20T02:52:57.293 に答える
7

Thread.Sleepは、正確なウェイクアップに使用するためのものではありません。実際、Windowsアーキテクチャ自体は、この種のことを目的としたものではありません。

于 2009-08-20T02:36:42.687 に答える
4

すぐに実験してみると、次のようなコードフラグメントに気づきました...

do {Debug.WriteLine(DateTime.Now.TimeOfDay.TotalMilliseconds.ToString()); } while(1);

同じ番号を複数回表示してから、複数回表示された新しい番号にジャンプします。これらの番号のセット間のギャップは一貫して15.625msであり、1000/64であることに気付きました。

Windowsタイマーの粒度は1/64秒のようです。それよりも良いものが必要な場合、私はあなたの痛みを感じますが、それはあなたがその中に収まらなければならないフレームワークです。(WindowsはハードリアルタイムOSではなく、そうであるとは主張していません)。

于 2009-12-15T07:27:40.347 に答える
2

スレッドのスリープとタイミング/スロットリングは非常に異なるものであり、適切に処理する必要があります。スレッドのスリープは一般的なタスクであり、システムが他のスレッドやプロセスに特定せずに実行する機会を与えることができます。一方、正確なタイミングを必要とするアプリケーションのスロットリングまたはスケジューリングタスクは、明示的なタイマーを使用して実行する必要があります。

時間的に正確なプロセスまたは同期が必要な場合は、Windowsの通常のプロセスでそれを達成するのに苦労することを覚えておいてください。正確なタイミングまたはスロットリングを正常に達成するには、Windowsのリアルタイムの優先順位を利用する必要があります。これは、Windowsが別のスレッドによってプリエンプトされた場合、いつでも任意のスレッドをスリープできるためです。

于 2009-08-20T03:06:44.120 に答える
1

少なくともxミリ秒スリープしたいアプリケーションでは、次のようなコードを使用しました。

public void Sleep(int milliseconds)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    while (stopwatch.ElapsedMilliseconds < milliseconds)
    {
        int timeout = milliseconds - stopwatch.ElapsedMilliseconds;
        Thread.Sleep(timeout >= 0 ? timeout : 0);
    }

    stopwatch.Stop();
}

Thread.Sleepがどれほど正確であるかに応じて、それはまったく正確ではありません。解像度は10msくらいだと思います。これほど長く「おおよそ」以外のことをすることは保証されていません。

于 2009-08-20T02:58:30.437 に答える
-1

たぶん、あなたはあなたがどこで成功したかを見つけるために時間の偏見に頼るべきではありません。試行回数を数え、これに対して評価することをお勧めします。

int tries;

for(tries=0; tries<3; tries++)
{
    Thread.Sleep(oneSecond);
}

Assert.GreaterOrEqual(tries, 3);
于 2009-12-15T08:02:30.307 に答える