我可以提高Thread.Sleep的分辨率吗?

时间:2011-09-30 19:06:23

标签: .net thread-sleep

Thread.Sleep()分辨率从1到15.6毫秒不等

鉴于此控制台应用程序:

class Program
{
    static void Main()
    {
        int outer = 100;
        int inner = 100;
        Stopwatch sw = new Stopwatch();

        for (int j = 0; j < outer; j++)
        {
            int i;
            sw.Restart();
            for (i = 0; i < inner; i++)
                Thread.Sleep(1);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

我预计输出将是100个接近100的数字。相反,我得到这样的结果:

99 99 99 100 99 99 99 106 106 99 99 99 100 100 99 99 99 99 101 99 99 99 99 99 101 99 99 99 99 101 99 99 99 100 99 99 99 99 99 103 99 99 99 99 100 99 99 99 99 813 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1560 1559 1559 1559 1559 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1559 1558 1558 1558 1558 1558

但有时我不会得到任何准确的结果;每次都是~1559。

为什么不一致?

有些谷歌搜索告诉我,15.6ms是时间片的长度,所以这解释了~1559的结果。但为什么我有时会得到正确的结果,有时我只得到15.6的倍数? (例如,Thread.Sleep(20)通常会给出~31.2ms)

如何受到硬件或软件的影响?

我问这个是因为我发现它的原因:

我一直在使用32位双核机器开发我的应用程序。今天我的机器升级到64位四核,完全重新安装操作系统。 (两种情况下都是Windows 7和.NET 4,但我不能确定旧机器有W7 SP1;新机器有。)

在新机器上运行我的应用程序后,我立即注意到我的表单需要更长时间才能淡出。我有一个自定义方法来淡化我的表单,它使用Thread.Sleep(),其值从10到50不等。在旧系统上,这似乎每次都很完美。在新系统上,褪色的时间要长得多。

为什么这个行为会在旧系统和新系统之间发生变化?这与硬件或软件有关吗?

我能否始终保持准确? (〜1ms分辨率)

我的程序中有什么东西可以让Thread.Sleep()可靠地精确到1ms左右吗?甚至10毫秒?

5 个答案:

答案 0 :(得分:7)

答案不是使用Thread.Sleep而是使用高分辨率计时器。你需要在繁忙的循环中进行淡入淡出,但听起来没问题。您根本不能指望来自Thread.Sleep的高分辨率,并且因在不同硬件上表现不同而臭名昭着。

如果硬件支持高性能计数器,则可以使用.net上的Stopwatch类。

答案 1 :(得分:7)

  

“Sleep函数暂停当前线程的执行至少指定的时间间隔。”

- &GT; http://social.msdn.microsoft.com/Forums/en/clr/thread/facc2b57-9a27-4049-bb32-ef093fbf4c29

答案 2 :(得分:3)

我可以回答我的一个问题:我能否始终保持准确? (〜1ms分辨率)

是的,似乎我可以使用timeBeginPeriod()和timeEndPeriod()。

我已经对此进行了测试,但确实有效。

我读过的一些内容表明,在应用程序的持续时间内调用timeBeginPeriod(1)是一个坏主意。但是,在方法开始时调用它然后在方法结束时使用timeEndPeriod()清除它应该没问题。

尽管如此,我还会调查使用计时器。

答案 3 :(得分:3)

您的计算机上正在运行一个正在调用timeBeginPeriod()和timeEndPeriod()的程序。通常是与媒体相关的程序,它使用timeSetEvent()来设置一个毫秒计时器。它也会影响Sleep()的分辨率。

您可以自己调整这些功能以获得一致的行为。但是对于UI效果来说并不是非常合理。它对笔记本电脑的电池寿命相当不友好。

睡眠20毫秒,实际上得到2/64秒是合乎逻辑的,cpu根本不会很快醒来,注意到20毫秒已经过去了。你只得到1/64秒的倍数。因此,实现衰落效果的Timer的合理选择是15毫秒,最坏情况下为64 fps动画。假设你的效果足够快。如果调用了timeBeginPeriod,你会有点不对,但不是很多。从时钟计算动画阶段也有效,但在我的书中有点矫枉过正。

答案 4 :(得分:-1)

你的做法是错误的。你永远不会让睡眠变得准确,你的机器越忙,你的睡眠循环就越错误。

你应该做的是看已经过了多少时间并相应地进行调整。虽然围绕睡眠旋转是一个坏主意,但“更好”的做法会更加复杂。我会保持建议的解决方案简单。

  • 调用DateTime.Now.Ticks并将其保存在变量(startTick)中。
  • 在你的循环中,按照你正在做的那样调用Sleep。
  • 再次调用DateTime.Now.Ticks并从中减去startTick - 此值将是自您启动淡入淡出(timeSinceStart)以来经过的100纳秒单位时间。
  • 使用timeSinceStart计算在经过那段时间后应该消耗多少。
  • 重复直至完全褪色。