我遇到了间歇性失败的单元测试,因为经过的时间不是我预期的那样。
此测试的示例如下:
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秒。
答案 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);