有趣的是,你的代码在我的四核Win7上完美地运行,几乎每次都生成恰好相隔2毫秒的值。
因此,我进行了更彻底的测试。这是我用Thread.Sleep(1)
进行的示例输出。代码在循环中打印连续调用DateTime.UtcNow
之间的毫秒数:
每行包含100个字符,因此代表“干净运行”上的100ms时间。因此,此屏幕涵盖大约2秒钟。最长的抢占时间为4ms;而且,在持续约1秒钟的时间内,每次迭代都需要精确1ms。那几乎就像实时操作系统的质量!1 :)
于是我再试一次,这次使用Thread.Sleep(2)
:
再次得到几乎完美的结果。这次每行长200ms,并且有一次近3秒钟的运行,其中缝隙永远不会超过2ms。
自然,下一步需要看的是我的机器上DateTime.UtcNow
的实际分辨率。这里没有任何睡眠的运行;如果UtcNow
根本没有改变,则会打印“。”:
最后,在调查在同一台机器上时间戳相隔15ms的奇怪情况时,我遇到了以下有趣的情况:
Windows API中有一个名为timeBeginPeriod
的函数,应用程序可以使用它来临时增加计时器频率,因此这可能是发生的情况。计时器分辨率的详细文档可通过硬件开发中心档案,特别是Timer-Resolution.docx(Word文件)进行查看。
结论:
DateTime.UtcNow
的分辨率可能比15ms高得多Thread.Sleep(1)
可能确实会睡眠1ms- 在我的机器上,
UtcNow
每次增加精确1ms时间(除去一个舍入误差-反编译器显示UtcNow
中有个除法运算)。 - 进程可能在低分辨率模式和一切都是基于15.6ms的高分辨率模式之间进行切换。
以下是代码:
static void Main(string[] args)
{
Console.BufferWidth = Console.WindowWidth = 100;
Console.WindowHeight = 20;
long lastticks = 0;
while (true)
{
long diff = DateTime.UtcNow.Ticks - lastticks;
if (diff == 0)
Console.Write(".");
else
switch (diff)
{
case 10000: case 10001: case 10002: Console.ForegroundColor=ConsoleColor.Red; Console.Write("1"); break;
case 20000: case 20001: case 20002: Console.ForegroundColor=ConsoleColor.Green; Console.Write("2"); break;
case 30000: case 30001: case 30002: Console.ForegroundColor=ConsoleColor.Yellow; Console.Write("3"); break;
default: Console.Write("[{0:0.###}]", diff / 10000.0); break;
}
Console.ForegroundColor = ConsoleColor.Gray;
lastticks += diff;
}
}
事实证明存在一个未记录的函数可以改变计时器分辨率。我还没有详细调查,但我想在这里发布一个链接:NtSetTimerResolution
。
1当然,我确保操作系统尽可能处于空闲状态,并且它有四个相当强大的CPU核心可供使用。如果我将所有四个核心负载到100%,情况就完全不同了,到处都是长时间的抢占。