DateTime.Now多久更新一次?或者是否有更精确的API来获取当前时间?

时间:2008-11-21 01:51:56

标签: c# .net datetime real-time

我在循环中运行代码,并根据当前时间保存状态。有时这可能只相差几毫秒,但由于某些原因,似乎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);
}

是否有另一种解决方案可以获得精确的当前时间到毫秒?

有人建议查看秒表课程。虽然秒表课程非常准确,但它并没有告诉我当前的时间,这是我需要的东西,以便保存我的程序状态。

7 个答案:

答案 0 :(得分:41)

奇怪的是,你的代码在我的Win7下的四核上运行得非常好,几乎每次都会产生相距2毫秒的值。

所以我做了一个更全面的测试。这是Thread.Sleep(1)的示例输出。代码打印循环中连续调用DateTime.UtcNow之间的ms数:

sleep 1

每行包含100个字符,因此在“干净运行”中表示100毫秒的时间。所以这个屏幕大约需要2秒钟。最长的抢占时间是4毫秒;此外,当每次迭代花费1毫秒时,有一个持续约1秒的周期。这几乎是实时的操作系统质量! 1 :)

所以这次我再次尝试Thread.Sleep(2)

sleep 2

再次,几乎完美的结果。这一次每行的长度为200毫秒,并且运行时间差不多是3秒,其中差距绝对不是2毫秒。

当然,接下来要看的是我机器上DateTime.UtcNow的实际分辨率。这是一场完全没有睡觉的跑步;如果.未完全更改,则会打印UtcNow

no sleep

最后,在调查一个奇怪的时间戳相差15ms的情况下,在产生上述结果的同一台机器上,我遇到了以下奇怪的事情:

enter image description here enter image description here

Windows API中有一个名为timeBeginPeriod的函数,应用程序可以使用它暂时增加计时器频率,因此这可能就是这里发生的事情。有关计时器分辨率的详细文档可通过Hardware Dev Center Archive获得,特别是Timer-Resolution.docx(Word文件)。

结论:

  • DateTime.UtcNow 可以的分辨率高于15毫秒
  • Thread.Sleep(1) 可以完全睡眠1毫秒
  • 在我的机器上,UtcNow一次增长正好1ms(给出或采取舍入误差 - Reflector显示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%,则图片会完全改变,并且在任何地方都会有长时间的抢占。

答案 1 :(得分:10)

处理毫秒时DateTime的问题根本不是由于DateTime类,而是与CPU标记和线程切片有关。本质上,当调度程序暂停操作以允许其他线程执行时,它必须在恢复之前至少等待1个时间片,这在现代Windows操作系统上大约需要15ms。因此,任何暂停低于15ms精度的尝试都会导致意外结果。

答案 2 :(得分:2)

如果您在做任何事情之前拍摄当前时间的快照,您可以将秒表添加到您存储的时间,不是吗?

答案 3 :(得分:1)

你应该问问自己,你是否真的需要准确的时间,或者只是关闭足够的时间加上一个增加的整数。

你可以通过在等待事件(例如互斥,选择,轮询,等待*等)之后立即获取()然后添加序列号(可能在纳秒范围内或任何位置)来做好事。室。

你也可以使用rdtsc机器指令(有些库为此提供了一个API包装器,不确定在C#或Java中执行此操作)以便从CPU获得便宜的时间并将其与现在的时间()相结合。 rdtsc的问题在于,在具有速度扩展的系统上,您永远无法确定它将要做什么。它也很快包裹起来。

答案 4 :(得分:0)

我用来完成100%准确完成此任务的所有内容都是计时器控件和标签。

代码不需要太多解释,相当简单。 全局变量:

int timer = 0;

这是刻度事件:

private void timeOfDay_Tick(object sender, EventArgs e)
    {

        timeOfDay.Enabled = false;
        timer++;

        if (timer <= 1)
        {
            timeOfDay.Interval = 1000;
            timeOfDay.Enabled = true;             
            lblTime.Text = "Time: " + DateTime.Now.ToString("h:mm:ss tt");                  
            timer = 0;
        }


}

这是表单加载:

private void DriverAssignment_Load(object sender, EventArgs e)
    {


        timeOfDay.Interval= 1;
        timeOfDay.Enabled = true;


}

答案 5 :(得分:0)

在回答有关更精确API的问题的第二部分时,AnotherUser的comment引导我找到这个解决方案,在我的场景中克服了DateTime.Now精度问题:

static FileTime time;        

public static DateTime Now()
{
    GetSystemTimePreciseAsFileTime(out time);
    var newTime = (ulong)time.dwHighDateTime << (8 * 4) | time.dwLowDateTime;
    var newTimeSigned = Convert.ToInt64(newTime);
    return new DateTime(newTimeSigned).AddYears(1600).ToLocalTime();
}        

public struct FileTime
{
    public uint dwLowDateTime;
    public uint dwHighDateTime;
}

[DllImport("Kernel32.dll")]
public static extern void GetSystemTimePreciseAsFileTime(out FileTime lpSystemTimeAsFileTime);

在我自己的基准测试中,迭代1M,它返回平均3个滴答与DateTime.Now 2滴答。

为什么1600不在我的管辖范围内,但我用它来获得正确的年份。

编辑:这仍然是win10的一个问题。任何有兴趣的人都可以使用这种证据:

void Main()
{
   for (int i = 0; i < 100; i++)
   {        
       Console.WriteLine(Now().ToString("yyyy-MM-dd HH:mm:ss.fffffff"));
       Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff"));
       Console.WriteLine();
   }    
}
// include the code above

答案 6 :(得分:-3)

您可以使用DateTime.Now.Ticks,阅读MSDN

上的文章

“单个刻度表示一百纳秒或一千万分之一秒。”