Thread.Sleep(1)花费超过1ms

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

来自Msdn

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

  • 我可以提高Thread.Sleep的分辨率吗?
  • Thread.Sleep(1)在C#中有什么影响?
  • 使用Thread.Sleep短时间的问题

共识是默认情况下,Sleep()至少需要15ms,除非您使用timeBegin / EndPeriod()设置分辨率,如此答案所示 。

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

Thread.Sleep(1)会让一个线程hibernate1ms,但你通常无法获得该精度。

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

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