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

lxkprmvk  于 2022-12-24  发布在  .NET
关注(0)|答案(5)|浏览(197)

我有一个关于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);
             }
         }
     }
 }
v9tzhpje

v9tzhpje1#

只是一个帖子来确认马修的正确答案。线程的准确性。睡眠()受Windows上的时钟中断率影响。默认情况下,每秒滴答64次,每15.625毫秒一次。()只有在这样的中断发生时才能完成,这里的心理意象是由“睡眠”这个词诱发的,处理器实际上是休眠的,不执行代码。只有那个时钟中断才会再次唤醒它,继续执行你的代码。
你选择的1000/60是一个非常不满意的选择,它要求16毫秒,略高于15.625,所以你总是在至少2个滴答之后醒来:2 x 15.625 = 31毫秒。您测量的值。
然而,中断率不是固定的,它可以由程序更改。它通过调用CreateTimerQueueTimer()或遗留timeBeginPeriod()来实现。浏览器通常需要这样做。一些简单的事情,如动画GIF需要一个更好的计时器,因为GIF帧时间是以10毫秒为单位指定的。或者通常任何与多媒体相关的操作都需要它。
一个程序这样做的一个非常丑陋的副作用是,时钟中断率的增加会对整个系统产生影响。就像你的程序一样。你的计时器突然变得准确,你实际上得到了你要求的睡眠时间,16毫秒。所以Chrome将频率改为每秒1000次。这是支持的最大值。如果你有一个竞争对手的操作系统,则是good for business
你可以通过选择一个更接近默认中断率的睡眠时间来避免这个问题。如果你要求15,你会得到15.625,Chrome对此没有影响。31是下一个最佳点。等等,15.625的整数倍和向下舍入。
更新:请注意,这种行为改变了just recently。从Win10版本2004开始,这种影响不再是全局的,所以Chrome不再影响你的程序。从Win 11开始,一个有非活动窗口的应用程序将以默认的中断率运行。

bqujaahr

bqujaahr2#

发生这种情况的原因可能是Chrome(或Chrome的某些组件)调用timeBeginPeriod()时使用的值增加了Windows API函数Sleep()(从Thread.Sleep()调用)的分辨率。
更多信息请参见此主题:Can I improve the resolution of Thread.Sleep?
几年前,我注意到Windows Media Player的这种行为:我们的一个应用程序的行为根据Windows Media Player是否运行而改变,结果是WMP调用了timeBeginPeriod()
然而,一般来说,Thread.Sleep()(以及Windows API Sleep())是非常不准确的。

5ktev3wc

5ktev3wc3#

基本上,Thread.Sleep isn't very accurate
Thread.Sleep(1000/60)(计算结果为Thread.Sleep(16)),要求线程进入睡眠状态,并在经过16毫秒后返回。但是,该线程可能要等到经过更长的时间后才能再次执行;例如32 ms。
至于为什么Chrome会产生影响,我不知道,但由于Chrome为每个标签生成一个新线程,它将对系统的线程行为产生影响。

mzmfm0qo

mzmfm0qo4#

首先,1000 / 60 = 16毫秒
PC时钟的分辨率约为18- 20 ms,Sleep()DateTime.Now的结果将四舍五入为该值的倍数。
因此,Thread.Sleep(5)Thread.Sleep(15)将延迟相同的时间,可以是20、40甚至60 ms。您无法得到太多保证,Sleep()的参数只是最小值。
另一个占用CPU(哪怕是一点点)的进程(Chrome)也会影响程序的行为。这和你看到的正好相反,所以这里发生了一些其他的事情。尽管如此,这是关于舍入到时间片的。

t3irkdon

t3irkdon5#

你遇到了DateTime的分辨率问题。你应该使用Stopwatch来实现这种精度。Eric Lippert statesDateTime只精确到30毫秒左右,所以在这种情况下你用它读的数据不会告诉你任何东西。
测量是问题的一半。循环的实际时间变化是由于Sleep分辨率(如其他答案所述)。

相关问题