Thread.Sleep(1)花费的时间超过1毫秒

时间:2013-09-28 12:21:03

标签: c# .net

我搜索了这个问题,但没有看到答案。如果它是重复的,我很乐意关闭它。

我目前正在尝试对某项技术进行一些性能评估,并看到一些相当令人难以置信的结果,所以我决定尝试一些。在那里,我想尝试看看秒表类是否正在返回我的预期。

Stopwatch sw = Stopwatch.StartNew();
Thread.Sleep(1);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

在这种情况下,我几乎看到了15毫秒的返回值。我理解DateTime的分辨率在那附近但是不应该Thread.Sleep(1)睡一个线程1ms?我所使用的系统返回Stopwatch.IsHighResolution为true并在.NET 4中运行。

背景: 此代码的完整和正确形式旨在收集有关Aerospike数据库获取请求的一些数字。数据库不在同一个盒子里。当我在查询处于中间时打印出sw.ElapsedMilliseconds时,我看到的主要是亚毫秒响应,这听起来有点怀疑,因为我的Java等效代码在大多数情况下返回更加可信的5ms-15ms响应。 Java代码使用的是System.nanoTime()的区别。我的C#代码中的submilli响应是指Console.WriteLine(sw.ElapsedMilliseconds)打印0。

6 个答案:

答案 0 :(得分:28)

计时器其他而不是秒表会被时钟中断递增。默认情况下,Windows上每秒运行64次。或15.625毫秒。所以一个小于16的Thread.Sleep()参数不会给你你所寻找的延迟,你总是会得到至少15.625的间隔。类似地,如果您读取,例如,Environment.TickCount或DateTime.Now并等待 less 超过16毫秒,那么您将读回相同的值并认为已经过了0毫秒。

始终使用秒表进行小增量测量,它使用不同的频率源。它的分辨率是可变的,它取决于主板上的芯片组。但你可以依靠它优于微秒。 Stopwatch.Frequency为您提供费率。

时钟中断率可以更改,你必须对timeBeginPeriod()进行pinvoke。这可以让你降到一毫秒,实际上使Thread.Sleep(1)准确。最好不要这样做,这是非常不友好的。

答案 1 :(得分:5)

我希望Thread.Sleep只不过是围绕Win32 Sleep API的托管包装器。众所周知,该API函数具有低分辨率。文档说明了这一点:

  

此函数使线程放弃其时间片的剩余部分,并在基于dwMilliseconds值的时间间隔内变得不可运行。系统时钟以恒定速率“滴答”。如果dwMilliseconds小于系统时钟的分辨率,则线程可能会睡眠时间少于指定的时间长度。如果dwMilliseconds大于一个tick但小于2,则等待可以是一到两个滴答之间的任何位置,依此类推。要提高休眠间隔的准确性,请调用timeGetDevCaps函数以确定支持的最小计时器分辨率,并调用timeBeginPeriod函数将计时器分辨率设置为最小值。调用timeBeginPeriod时请小心,因为频繁的调用会显着影响系统时钟,系统电源使用和调度程序。如果你调用timeBeginPeriod,请在应用程序的早期调用它一次,并确保在应用程序的最后调用timeEndPeriod函数。

     

休眠间隔过后,线程就可以运行了。如果指定0毫秒,则线程将放弃其时间片的剩余部分但仍保持准备状态。请注意,不保证立即运行就绪线程。因此,线程可能在睡眠间隔过去一段时间后才会运行。有关更多信息,请参阅计划优先级。

因此,您可以根据建议使用timeBeginPeriodtimeEndPeriod来提高计时器分辨率,但实际上并不建议这样做。如果你真的需要短时间阻止,那么我建议你找到一个更好的解决方案。例如多媒体计时器。

答案 2 :(得分:1)

是的,你已经想到的是绝对正确的。

Thread.Sleep不保证在N毫秒发布。它将确保SleepN毫秒之前不会释放。

换句话说,Thread.Sleep(1)表示睡眠至少1毫秒。操作系统不会在特定时间内为特定线程安排时间片。

  

在指定的时间内,操作系统不会安排线程执行。此方法将线程的状态更改为包括WaitSleepJoin。

来自Msdn

答案 3 :(得分:1)

搜索“线程睡眠解决方案”会显示这些相关的SO问题:

默认情况下,默认情况下,Sleep()至少需要15ms,除非您使用timeBegin / EndPeriod()设置分辨率,如此answer

答案 4 :(得分:0)

Thread.Sleep(n)表示阻止当前线程至少在n毫秒内可以发生的次数(或线程量子)。时间片的长度在不同版本/类型的Windows和不同处理器上是不同的,通常在15到30毫秒的范围内。这意味着线程几乎可以保证阻塞超过n毫秒。你的线程在n毫秒后完全重新唤醒的可能性几乎是不可能的。因此,Thread.Sleep对于计时毫无意义。

答案 5 :(得分:0)

Thread.Sleep(1)会让一个线程休眠1ms,但你通常无法达到这个精度。

但是,一旦线程进入休眠状态,内核需要时间将其放回就绪队列,然后在线程实际再次运行时将其置于运行状态。

所有这些因素都取决于平台,CPU的数量,其他进程的数量等。对于非RT操作系统,您无法获得任何保证。