Thread.Sleep(TimeSpan)的准确度如何?

时间:2009-08-20 02:33:54

标签: c# multithreading sleep stopwatch

我遇到了间歇性失败的单元测试,因为经过的时间不是我预期的那样。

此测试的示例如下:

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);

大部分时间都过去了,但至少有一次失败,因为:

预期:大于或等于2999 但是:2998

我不明白它怎么可能不到3秒。 Thread.Sleep或者我不知道的秒表是否存在准确性问题?

正如下面一些问题的更新。正在进行单元测试的场景是一个类,它允许一个方法调用一个方法来执行某些操作,如果它失败则等待一秒并调用该方法。上面显示的测试只是对正在发生的事情的近似。

说我想调用一个方法DoSomething()...但是如果DoSomething()抛出异常,我希望能够重试最多调用它3次但等待1秒之间每次尝试。在这种情况下,单元测试的目的是验证当我们请求3次重试,每次重试之间等待1秒钟时,所花费的总时间大于3秒。

6 个答案:

答案 0 :(得分:15)

您的线程与其他线程共享CPU时间。睡眠将在你再次轮到你的时候结束,并且内核注意到睡眠时间已经过去,所以它不是那么准确。

CPU负载,进程优先级,并发线程数,甚至来自其他进程,都会对它产生影响。

答案 1 :(得分:8)

Thread.Sleep不适用于精确唤醒。实际上,Windows体系结构本身并不适用于此类事物。

答案 2 :(得分:3)

快速试验,我注意到代码片段如......

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

多次显示相同的数字,然后跳转到多次显示的新数字等。这些数字组之间的差距始终为15.625ms,我注意到它是1000/64。

看起来Windows计时器的粒度为1/64秒。如果你需要更好的话,那么我会感受到你的痛苦,但那是你必须适应的框架。 (Windows不是一个硬实时操作系统,并不声称是。)

答案 3 :(得分:2)

线程睡眠和计时/节流是非常不同的事情,应该得到适当的处理。休眠线程是一项常规任务,允许系统为其他线程和进程提供执行的机会,而无需具体说明。另一方面,应该使用显式计时器来限制应用程序或调度需要精确计时的任务。

请记住,如果您需要时间准确的流程或同步,您将很难通过Windows中的正常流程实现这一目标。您需要利用Windows实时优先级来成功实现准确的计时或限制,因为如果被另一个线程抢占,Windows可以随时睡眠任何线程。

答案 4 :(得分: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左右。除了“大约”这么久之外,我们无法做任何事情。

答案 5 :(得分:-1)

也许你不应该依赖时间偏见来找出你是否成功。更好地计算您的尝试次数并对此进行评估:

int tries;

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

Assert.GreaterOrEqual(tries, 3);