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

Igb*_*man 8 .net thread-sleep

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

鉴于此控制台应用:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我期望输出为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 99 99 99 99 99 99 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 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?甚至10ms?

Dav*_*nan 7

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

您可以使用Stopwatch.net上的类,如果硬件支持它们,则使用高分辨率性能计数器.

  • 我采用了这个解决方案。我通过在循环中检查 StopWatch.MillisecondsElapsed 来获得毫秒精度。正如您所说,在这种情况下,繁忙循环就可以了。 (2认同)

Mas*_*sim 7

"Sleep函数暂停执行当前线程至少指定的时间间隔."

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

  • 短语"至少"回答了你的直接问题(没有). (4认同)

Igb*_*man 3

我可以回答我的问题之一:我能否使其始终准确?(~1ms 分辨率)

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

我已经测试过这个并且它有效。

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

尽管如此,我也会研究如何使用计时器。