Thread.Sleep(TimeSpan)有多准确?

我遇到了一个间歇性失败的unit testing,因为时间已经不是我所期望的了。

这个testing是什么样子就是一个例子:

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

大多数情况下,这个过程至less失败了一次失败,因为:

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

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

就像下面一些问题的更新一样。 被unit testing的场景是一个类允许一个人调用一个方法来执行一些操作,如果它失败了,等一下再调用这个方法。 上面显示的testing只是近似发生的事情。

说我想调用一个方法DoSomething()…但是,如果DoSomething()引发exception,我希望能够重试调用它最多3次,但每次尝试之间等待1秒。 在这种情况下,unit testing的目的是validation当我们要求3次重试,每次重试之间等待1秒钟,总时间大于3秒。

您的线程正在与其他线程共享CPU时间。 睡眠一旦轮到你就会结束,内核注意到睡眠时间已经过去,所以不是那么准确。

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

Thread.Sleep并不打算用于精确唤醒。 真的,windows体系结构本身并不适合这种事情。

线程的睡眠和计时/节stream是非常不同的事情,应该适当地对待。 沉睡一个线程是一个普遍的任务,允许系统给其他线程和进程执行的机会,而不是具体的。 另一方面,节制应用程序或调度任务需要准确的时间,应该用明确的计时器来执行。

请记住,如果您需要时间精确的stream程或同步,您将很难通过Windows中的正常stream程来实现这一点。 您将需要利用Windows实时优先级成功实现准确的计时或限制,因为如果任何线程被其他线程抢占,则Windows可以随时hibernate。

快速试验,我注意到一个代码片段…

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

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

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

在一个我想睡至lessx毫秒的应用程序中,我使用了一些类似于下面的代码:

 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毫秒左右的地方。 除了“大约”这么长的时间以外,不能保证做任何事情。

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

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