为什么Thread.Sleep在运行其他应用程序时等待的时间比请求的长?

时间:2013-10-16 22:35:09

标签: c# .net multithreading behavior

我对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);
             }
         }
     }
 }

5 个答案:

答案 0 :(得分:8)

只是一个确认马修正确答案的帖子。 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个滴答。支持的最大值。当你有一个竞争的操作系统时,good for business

您可以通过选择与默认中断速率更接近的睡眠持续时间来避免此问题。如果您要求15,那么您将获得15.625而Chrome不会对此产生影响。 31是下一个甜蜜点。 Etcetera,15.625的整数倍和向下舍入。

答案 1 :(得分:3)

这可能是因为Chrome(或Chrome的某些组件)正在调用timeBeginPeriod(),其值增加了从Sleep()调用的Windows API函数Thread.Sleep()的分辨率

有关详细信息,请参阅此主题:Can I improve the resolution of Thread.Sleep?

几年前我注意到Windows Media Player的这种行为:我们的某个应用程序的行为根据Windows Media Player是否正在运行而发生了变化。事实证明,WMP正在调用timeBeginPeriod()

但是,一般情况下,Thread.Sleep()(以及Windows API Sleep()扩展名)非常不准确。

答案 2 :(得分:2)

首先,1000/60 = 16毫秒

PC时钟的分辨率约为18-20毫秒Sleep()DateTime.Now的结果将四舍五入为该值的倍数。

因此,Thread.Sleep(5)Thread.Sleep(15)会延迟相同的时间。这可能是20毫秒,40毫秒甚至60毫秒。你得不到多少保证,Sleep()的论点只是最小的。

另一个占用CPU(甚至一点点)的进程(Chrome)会以这种方式影响程序的行为。编辑:这与你所看到的相反,所以这里发生了一些其他事情。不过,这是关于围绕时代的问题。

答案 3 :(得分:2)

基本上,Thread.Sleep isn't very accurate

Thread.Sleep(1000/60)(评估为Thread.Sleep(16)),要求线程进入休眠状态,并在16ms过去后返回。但是,在更长的时间过去之前,该线程可能无法再次执行;比方说,例如,32ms。

至于为什么Chrome会产生影响,我不知道,但由于Chrome会为每个标签生成一个新线程,因此它会对系统的线程行为产生影响。

答案 4 :(得分:1)

您正在使用DateTime解决问题。您应该使用Stopwatch来获得这种精度。 DateTime Sleep只能精确到30毫秒左右,因此在这种情况下使用它的读数不会告诉你任何事情。

测量是问题的一半。循环的实际时间变化归因于{{1}}分辨率(如其他答案中所述)。