[英]How accurate is Thread.Sleep(TimeSpan)?
我遇到了间歇性失败的单元测试,因为经过的时间不是我预期的。
此测试的示例如下:
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或者Stopwatch是否存在准确性问题?
正如以下一些问题的更新。 正在进行单元测试的场景是一个类,它允许一个方法调用一个方法来执行某些操作,如果它失败则等待一秒并调用该方法。 上面显示的测试只是对正在发生的事情的近似。
假设我想调用方法DoSomething()...但是如果DoSomething()抛出异常,我希望能够重试最多调用它3次,但每次尝试之间等待1秒。 在这种情况下,单元测试的目的是验证当我们请求3次重试,每次重试之间等待1秒钟时,所花费的总时间大于3秒。
您的线程与其他线程共享CPU时间。 睡眠将在你再次轮到你的时候结束,并且内核注意到睡眠时间已经过去,所以它不那么准确。
CPU负载,进程优先级,并发线程数,甚至来自其他进程,都会对它产生影响。
Thread.Sleep不适用于精确唤醒。 实际上,Windows体系结构本身并不适用于此类事物。
快速试验,我注意到代码片段像...
do {Debug.WriteLine(DateTime.Now.TimeOfDay.TotalMilliseconds.ToString()); } while(1);
多次显示相同的数字,然后跳转到多次显示的新数字等。这些数字组之间的差距始终为15.625ms,我注意到它是1000/64。
看起来Windows计时器的粒度为1/64秒。 如果你需要更好的话,那么我会感受到你的痛苦,但那是你必须适应的框架。 (Windows不是一个硬实时操作系统,并不声称是)。
线程休眠和定时/节流是非常不同的事情,应该适当对待。 休眠线程是一项常规任务,允许系统为其他线程和进程提供执行的机会,而无需具体说明。 另一方面,应该使用显式计时器来限制应用程序或调度需要精确计时的任务。
请记住,如果您需要时间准确的流程或同步,您将很难通过Windows中的正常流程实现这一目标。 您需要利用Windows实时优先级来成功实现准确的计时或限制,因为如果被另一个线程抢占,Windows可以随时睡眠任何线程。
在我想睡至少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左右。 除了“近似”这么久之外,我们无法做任何事情。
也许你不应该依赖时间偏见来找出你是否成功。 更好地计算您的尝试次数并对此进行评估:
int tries;
for(tries=0; tries<3; tries++)
{
Thread.Sleep(oneSecond);
}
Assert.GreaterOrEqual(tries, 3);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.