5

关于 C# 中的线程,我有一个小问题。出于某种原因,当我打开 Chrome 时,我的线程从 32 毫秒延迟加速到 16 毫秒延迟,当我关闭 Chrome 时,它​​又回到 32 毫秒。我正在使用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);
             }
         }
     }
 }
4

5 回答 5

9

只是一个帖子来确认马修的正确答案。的准确性Thread.Sleep()受 Windows 上的时钟中断率影响。默认情况下每秒滴答 64 次,每 15.625 毫秒一次。`Sleep() 只有在发生此类中断时才能完成。这里的心理形象是由“睡眠”一词引起的,处理器实际上处于睡眠状态并且没有执行代码。只有那个时钟中断会再次唤醒它以继续执行您的代码。

你的选择1000/60是一个非常不愉快的选择,它要求 16 毫秒。刚刚结束,15.625所以你总是会在至少 2 个滴答声后醒来:2 x 15.625 = 31 msec. 你测量的。

但是,该中断率不是固定的,它可以通过程序进行更改。它通过调用CreateTimerQueueTimer()或 legacy来实现timeBeginPeriod()。一般来说,浏览器需要这样做。像 GIF 动画这样简单的事情需要更好的计时器,因为 GIF 帧时间以 10 毫秒为单位指定。或者一般来说,任何与多媒体相关的操作都需要它。

执行此操作的程序的一个非常难看的副作用是,增加的时钟中断率会对系统范围产生影响。就像在你的程序中一样。你的计时器突然变得准确,你实际上得到了你要求的睡眠持续时间,16 毫秒。因此 Chrome 正在将速率更改为每秒 1000 次滴答声。支持的最大值。当您拥有竞争性操作系统时,这对业务有好处。

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

于 2013-10-18T00:21:53.703 回答
4

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

有关更多信息,请参阅此线程:我可以提高 Thread.Sleep 的分辨率吗?

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

然而,一般来说,Thread.Sleep()(以及通过扩展,Windows API Sleep())是非常不准确的。

于 2013-10-16T23:11:59.557 回答
2

首先,1000 / 60 = 16 毫秒

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

因此,Thread.Sleep(5)并且Thread.Sleep(15)会延迟相同的时间。这可以是 20、40 甚至 60 毫秒。您没有得到太多保证, for 的论点Sleep()只是最低限度。

另一个占用 CPU(即使是一点点)的进程(Chrome)也会以这种方式影响程序的行为。编辑:这与您所看到的相反,所以这里发生了一些其他事情。不过,它是关于四舍五入到时间片。

于 2013-10-16T22:45:29.243 回答
2

基本上,Thread.Sleep不是很准确

Thread.Sleep(1000/60)(计算结果为Thread.Sleep(16)),要求线程进入睡眠状态并在 16 毫秒过去后返回。但是,该线程可能在经过更长的时间后才能再次执行;比如说,32ms。

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

于 2013-10-16T22:46:10.820 回答
1

您遇到了DateTime. 您应该使用Stopwatch这种精度。 Eric Lippert 指出DateTime它只能精确到 30 毫秒左右,所以在这种情况下,你的读数不会告诉你任何事情。

测量是您问题的一半。循环的实际时间变化是由于Sleep分辨率(如其他答案中所述)。

于 2013-10-16T22:45:19.990 回答