Thread.Sleep(TimeSpan) 有多准确?
Posted
技术标签:
【中文标题】Thread.Sleep(TimeSpan) 有多准确?【英文标题】:How accurate is Thread.Sleep(TimeSpan)? 【发布时间】:2010-11-21 04:18:30 【问题描述】:我遇到了一个间歇性失败的单元测试,因为经过的时间不是我所期望的。
此测试的示例如下:
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 秒。
【问题讨论】:
只是出于好奇,为什么你有一个以这种方式测试 Thread.Sleep() 的单元测试?我猜您实际上是在测试其他活动是否在 3 秒内完成,但为什么不直接调用 Thread.Sleep(3000)? @MusiGenesis 我已经更新了这个问题,试图澄清一下。 仅供参考。自从我发现了这个小代码,我到处使用它:codeproject.com/KB/cs/highperformancetimercshar.aspx 【参考方案1】:Thread.Sleep 不适用于精确唤醒。确实,windows 架构本身并不适合这种事情。
【讨论】:
【参考方案2】:您的线程正在与其他线程共享 CPU 时间。再次轮到您时,睡眠将结束,并且内核注意到睡眠时间已经过去,所以它不是那么准确。
CPU 负载、进程优先级、并发线程数,甚至来自其他进程,都会对其产生影响。
【讨论】:
但是如果它只是在内核注意到睡眠时间已经过去时才结束,它怎么可能比我预期的要花更少的时间呢?在我的情况下,3010ms 是可以理解和接受的,它在达到要求的时间之前就停止了......尽管是 1-2ms 实现可能会在您指定的时间前几毫秒设置唤醒时间。如果没有,它会总是稍后醒来。通过将时间设置得更早,它更有可能到达附近的某个地方。 @mezoid:我认为 PC 架构的时间精度并不高。旧 PC 每秒仅“滴答”18.2 次,因此使用的时钟和“滴答计数”仅每 52 毫秒更新一次。我相信它现在一定要快得多,但我仍然不确定它有多准确。 那么它通常在几秒钟内准确还是可以在长时间的睡眠间隔中减少几分钟? 在我的机器上,它的每边都在 10 毫秒内(不记得确切的 nr)如果你真的想你可以尽早安排并旋转 cpu 直到你达到目标【参考方案3】:在一个我想休眠至少 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 的准确程度,它根本不准确。我认为分辨率在 10 毫秒左右。除了“大约”这么长之外,不能保证做很多事情。
【讨论】:
您有任何关于该决议的参考资料吗?如果绝对是 10 毫秒,我可以将 2999 更改为 2990 ..... 由于其他人已经注意到的事情(例如影响它的处理器负载),没有硬性和快速的解决方案。基本上它归结为您的线程再次获得优先权以开始执行。我在几个地方看到的关于 SO 的轶事证据表明平均大约 10 毫秒。 ***.com/questions/1116249/… 和其他我读到的但现在找不到的答案。【参考方案4】:线程休眠和定时/节流是非常不同的事情,应该适当对待。休眠线程是一项通用任务,允许系统给其他线程和进程执行的机会,而无需具体说明。另一方面,限制应用程序或调度需要精确计时的任务应该使用显式计时器来执行。
请记住,如果您需要时间精确的进程或同步,您将很难在 Windows 中使用普通进程来实现。您需要利用 windows 实时优先级来成功实现准确的计时或节流,因为 windows 可以随时休眠任何线程,如果它被另一个线程抢占。
【讨论】:
我同意,Thread.Sleep 的意图是关于线程优先级,而不是与精确的时钟计时有关。在我们的 PC 中可以有一个铯处理时钟之前,不幸的是,精确(绝对)计时将不可用。 :Pen.wikipedia.org/wiki/Caesium 好吧,我希望大多数人不需要原子钟精确的计时。 :P 我认为大多数 CPU 中的时钟对于原子振动测量之外的大多数应用来说都足够精确。 :D【参考方案5】:快速试验,我注意到一个代码片段...
做 Debug.WriteLine( DateTime.Now.TimeOfDay.TotalMilliseconds.ToString() ); 而 ( 1 );
多次显示相同的数字,然后跳转到多次显示的新数字,等等。这些数字集之间的差距始终是 15.625 毫秒,我注意到是 1000 / 64。
看起来 Windows 计时器的粒度为 1/64 秒。如果您需要比这更好的,那么我会感觉到您的痛苦,但这是您必须适应的框架。 (Windows 不是硬实时操作系统,也没有声称是)。
【讨论】:
【参考方案6】:也许你不应该依靠时间偏差来判断你是否成功。更好地计算您的尝试次数并以此进行评估:
int tries;
for(tries=0; tries<3; tries++)
Thread.Sleep(oneSecond);
Assert.GreaterOrEqual(tries, 3);
【讨论】:
这将测试什么?那个 for 循环可以正常工作吗?以上是关于Thread.Sleep(TimeSpan) 有多准确?的主要内容,如果未能解决你的问题,请参考以下文章
从 Thread.Sleep() 制作 Task.Delay() [重复]