我有一个循环运行的代码,它根据当前时间保存状态.有时这可能只相差几毫秒,但由于某些原因,似乎DateTime.Now将始终返回至少10 ms的值,即使它仅在2或3 ms之后.这是一个主要问题,因为我保存的状态取决于它保存的时间(例如录制内容)
我的测试代码返回10 ms的每个值:
public static void Main() { var dt1 = DateTime.Now; System.Threading.Thread.Sleep(2); var dt2 = DateTime.Now; // On my machine the values will be at least 10 ms apart Console.WriteLine("First: {0}, Second: {1}", dt1.Millisecond, dt2.Millisecond); }
是否有另一种解决方案可以获得精确的当前时间到毫秒?
有人建议看秒表类.虽然秒表类是非常准确的,但它并没有告诉我当前的时间,我需要的东西是为了保存程序的状态.
奇怪的是,你的代码在我的Win7下的四核上运行得非常好,几乎每次都产生相距2毫秒的值.
所以我做了一个更全面的测试.这是我的示例输出Thread.Sleep(1)
.代码打印DateTime.UtcNow
循环中连续调用之间的ms数:
每行包含100个字符,因此在"干净运行"中表示100毫秒的时间.所以这个屏幕大约需要2秒钟.最长的抢占时间是4毫秒; 此外,当每次迭代花费1毫秒时,有一个持续约1秒的周期.这几乎是实时操作系统的质量!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的时候,进程可以切换到低分辨率模式,而高速分辨率模式,有1ms分片,可以在运行中进行切换.
这是代码:
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%,则图像会完全改变,并且在任何地方都会有长时间的抢占.
处理毫秒时DateTime的问题不是由于DateTime类,而是与CPU标记和线程切片有关.本质上,当调度程序暂停操作以允许其他线程执行时,它必须在恢复之前至少等待1个时间片,这在现代Windows操作系统上大约需要15ms.因此,任何暂停低于15ms精度的尝试都会导致意外结果.