为什么Thread.Sleep在运行其他应用程序时等待的时间比请求的长?
关于C#中的线程,我有一个小问题。 出于某种原因,当我打开Chrome时,我的线程从32ms延迟加速到16ms延迟,当我关闭Chrome时它会回到32ms。 我正在使用Thread.Sleep(1000 / 60)
来延迟。 有人可以解释为什么会这样,并可能建议一个可能的解决方案?
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication2 { class Program { static bool alive; static Thread thread; static DateTime last; static void Main(string[] args) { alive = true; thread = new Thread(new ThreadStart(Loop)); thread.Start(); Console.ReadKey(); } static void Loop() { last = DateTime.Now; while (alive) { DateTime current = DateTime.Now; TimeSpan span = current - last; last = current; Console.WriteLine("{0}ms", span.Milliseconds); Thread.Sleep(1000 / 60); } } } }
只是一个post来确认马修的正确答案。 Thread.Sleep()
的准确性受Windows上时钟中断率的影响。 它默认为每秒64次,每15.625毫秒一次。 ‘Sleep()只能在发生这种中断时完成。 这里的心理图像是由“睡眠”一词引起的,处理器实际上是睡着而不是执行代码。 只有那个时钟中断才会再次唤醒它以恢复执行代码。
你选择1000/60
是一个非常不开心的,要求16毫秒。 仅15.625
所以你总是至少在2个刻度后唤醒: 2 x 15.625 = 31 msec
。 你测量了什么。
然而,该中断率并不固定,可以由程序改变。 它通过调用CreateTimerQueueTimer()
或遗留的timeBeginPeriod()
。 浏览器通常需要这样做。 像GIF动画一样简单需要更好的定时器,因为GIF帧时间以10毫秒为单位指定。 或者通常,任何与多媒体相关的操作都需要它。
程序执行此操作的一个非常难看的副作用是这种增加的时钟中断率具有系统范围的影响。 就像你在程序中一样。 你的计时器突然变得准确,你实际上得到了你要求的睡眠持续时间,16毫秒。 所以Chrome正在将速率改为每秒1000个滴答。 支持的最大值。 当您拥有竞争的操作系统时, 对业务有益 。
您可以通过选择与默认中断速率更接近的睡眠持续时间来避免此问题。 如果您要求15,那么您将获得15.625而Chrome无法对此产生影响。 31是下一个甜蜜点。 Etcetera,15.625的整数倍和向下舍入。
这可能是因为Chrome(或Chrome的某些组件)正在调用timeBeginPeriod()
,其值增加了Windows API函数Sleep()
的分辨率,该函数从Thread.Sleep()
调用。
有关更多信息,请参阅此主题: 我可以提高Thread.Sleep的分辨率吗?
几年前我注意到了Windows Media Player的这种行为:我们的一个应用程序的行为根据Windows Media Player是否正在运行而发生了变化。 事实certificate,WMP正在调用timeBeginPeriod()
。
但是,通常, Thread.Sleep()
(以及扩展名,Windows API Sleep()
)非常不准确。
基本上, Thread.Sleep
不是很准确 。
Thread.Sleep(1000/60)
(评估为Thread.Sleep(16)
),要求线程进入hibernate状态,并在16ms过去后返回。 但是,在更长的时间过去之前,该线程可能无法再次执行; 比方说,例如,32ms。
至于为什么Chrome有效,我不知道,但由于Chrome为每个标签生成一个新线程,它将对系统的线程行为产生影响。
首先,1000/60 = 16毫秒
PC时钟的分辨率约为18-20ms, Sleep()
和DateTime.Now
的结果将四舍五入为该值的倍数。
因此, Thread.Sleep(5)
和Thread.Sleep(15)
将延迟相同的时间。 这可能是20,40或甚至60毫秒。 你得不到多少保证, Sleep()
的参数只是最小的。
另一个占用CPU(甚至一点点)的进程(Chrome)会以这种方式影响程序的行为。 编辑:这与你所看到的相反,所以在这里发生了一些其他事情。 不过,这是关于围绕时代的问题。
您正在使用DateTime
解决问题。 你应该使用Stopwatch
来达到这种精度。 Eric Lippert声称 DateTime
只能精确到30毫秒左右,因此在这种情况下你的读数不会告诉你什么。
测量是问题的一半。 循环的实际时间变化是由Sleep
分辨率引起的(如其他答案中所述)。