【问题标题】:Windows system time with millisecond precision毫秒精度的 Windows 系统时间
【发布时间】:2010-06-29 13:00:26
【问题描述】:

Related to my previous question,但是对于 C#,我需要精确的系统时间,包括毫秒。

C# 时间函数的精度高达 10 到 15 毫秒,但不完全是 1 毫秒。

队列性能计数器也是如此。有没有其他方法可以精确到毫秒?

【问题讨论】:

    标签: c# .net windows datetime


    【解决方案1】:

    Windows 不想每秒更新 1000 次系统时钟来浪费电力,所以默认是每秒只更新 60-100 次。如果将多媒体定时器设置为 1ms,则可以从时钟获得 1ms 的分辨率,但不建议这样做。

    为了详细说明节电,当 CPU 闲置一段时间时会发生什么情况,即它可以进入非常低功耗的状态。每当它被中断(例如增加时钟滴答声)时,它必须离开其极低功耗状态并使用大量电力来为整个 CPU 供电以服务该中断。换句话说,额外的功能不是增加时钟滴答,而是让 CPU 保持清醒。

    由于我的笔记本电脑在时钟频率为 60Hz 时使用 10W,而在 1000Hz 时使用 11W,而我的电池续航时间为 300 分钟,因此较慢的时钟使我的电池续航时间增加了近 30 分钟!

    【讨论】:

    • Nitrodist:是的,这是对同一问题的解释。
    • 他说的是内核节拍,一个内核节拍是一个预定的硬件(CPU)timer interrupt。尽管 Windows 在电力效率方面远非擅长(比 android 效率低 50%,比 mac OS 低 20%),但它确实没有理由让它变得更糟。内核滴答用于调用过期(或合并)的计时器回调、重新更新线程/进程调度以及增加全局操作系统滴答计数。
    • 这如何回答这个问题?您正在解释为什么 DateTime.Now 不够准确,但您没有展示如何提高准确性...
    【解决方案2】:

    您可以使用 this DateTimePrecise class 在 .NET 中获得高精度时间

    更新
    CodeProject 链接不再有效。我已提取代码from archive.org 并将其嵌入此处以供将来参考。此代码“按原样”包含在此处,与 CodeProject 页面中包含的完全相同。

    DateTimePreciseDateTime.Now一样好用,只不过@9​​87654326@是实例方法而不是静态方法,所以你要先实例化一个DateTimePrecise

    using System.Diagnostics;
    
    /// DateTimePrecise provides a way to get a DateTime that exhibits the
    /// relative precision of
    /// System.Diagnostics.Stopwatch, and the absolute accuracy of DateTime.Now.
    public class DateTimePrecise
    {
        /// Creates a new instance of DateTimePrecise.
        /// A large value of synchronizePeriodSeconds may cause arithmetic overthrow
        /// exceptions to be thrown. A small value may cause the time to be unstable.
        /// A good value is 10.
        /// synchronizePeriodSeconds = The number of seconds after which the
        /// DateTimePrecise will synchronize itself with the system clock.
        public DateTimePrecise(long synchronizePeriodSeconds)
        {
            Stopwatch = Stopwatch.StartNew();
            this.Stopwatch.Start();
    
            DateTime t = DateTime.UtcNow;
            _immutable = new DateTimePreciseSafeImmutable(t, t, Stopwatch.ElapsedTicks,
                Stopwatch.Frequency);
    
            _synchronizePeriodSeconds = synchronizePeriodSeconds;
            _synchronizePeriodStopwatchTicks = synchronizePeriodSeconds *
                Stopwatch.Frequency;
            _synchronizePeriodClockTicks = synchronizePeriodSeconds *
                _clockTickFrequency;
        }
    
        /// Returns the current date and time, just like DateTime.UtcNow.
        public DateTime UtcNow
        {
            get
            {
                long s = this.Stopwatch.ElapsedTicks;
                DateTimePreciseSafeImmutable immutable = _immutable;
    
                if (s < immutable._s_observed + _synchronizePeriodStopwatchTicks)
                {
                    return immutable._t_base.AddTicks(((
                        s - immutable._s_observed) * _clockTickFrequency) / (
                        immutable._stopWatchFrequency));
                }
                else
                {
                    DateTime t = DateTime.UtcNow;
    
                    DateTime t_base_new = immutable._t_base.AddTicks(((
                        s - immutable._s_observed) * _clockTickFrequency) / (
                        immutable._stopWatchFrequency));
    
                    _immutable = new DateTimePreciseSafeImmutable(
                        t,
                        t_base_new,
                        s,
                        ((s - immutable._s_observed) * _clockTickFrequency * 2)
                        /
                        (t.Ticks - immutable._t_observed.Ticks + t.Ticks +
                            t.Ticks - t_base_new.Ticks - immutable._t_observed.Ticks)
                    );
    
                    return t_base_new;
                }
            }
        }
    
        /// Returns the current date and time, just like DateTime.Now.
        public DateTime Now
        {
            get
            {
                return this.UtcNow.ToLocalTime();
            }
        }
    
        /// The internal System.Diagnostics.Stopwatch used by this instance.
        public Stopwatch Stopwatch;
    
        private long _synchronizePeriodStopwatchTicks;
        private long _synchronizePeriodSeconds;
        private long _synchronizePeriodClockTicks;
        private const long _clockTickFrequency = 10000000;
        private DateTimePreciseSafeImmutable _immutable;
    }
    
    internal sealed class DateTimePreciseSafeImmutable
    {
        internal DateTimePreciseSafeImmutable(DateTime t_observed, DateTime t_base,
             long s_observed, long stopWatchFrequency)
        {
            _t_observed = t_observed;
            _t_base = t_base;
            _s_observed = s_observed;
            _stopWatchFrequency = stopWatchFrequency;
        }
        internal readonly DateTime _t_observed;
        internal readonly DateTime _t_base;
        internal readonly long _s_observed;
        internal readonly long _stopWatchFrequency;
    }
    

    【讨论】:

    • @Răzvan 我已经从 archive.org 恢复了代码并将其添加到我的答案中。感谢您指出这是一个死链接。
    • 使用此代码时要小心。尝试除以零时会抛出异常。
    • @Dan H:你能举一个抛出异常的例子吗?
    • 我可以使用这个类来比较两个不同应用程序的时间吗?即我不仅需要微秒精度,还需要微秒精度
    • @javapowered 时间应该合理准确。该类使用Date.UtcNow 作为其基础,然后使用系统的高精度计时器计算自UtcNow 以来的时间。因为它源自DateTime.UtcNow,所以它永远不会比这更准确。您还应该检查您的硬件的Stopwatch.Frequency,以确定高精度功能是否确实有效。
    【解决方案3】:

    尝试System.Diagnostics.Stopwatch 以获得高分辨率计时。

    如果安装的硬件和 操作系统支持 高分辨率性能计数器, 然后秒表类使用它 计数器来测量经过的时间。 否则,秒表类使用 测量经过的系统计时器 时间。

    尝试原生DateTime.Ticks,系统时间精度高达一百纳秒; 1 毫秒 = 10000 个滴答声。

    while (true)
    {
        System.Threading.Thread.Sleep(1);
    
        Console.WriteLine("{0} {1}",
            System.DateTime.Now.Ticks,
            System.DateTime.Now.ToString("ss:fff"));
    }
    
    PS > .\test.exe
        634134152924322129 52:432
        634134152924332129 52:433
        634134152924342130 52:434
        634134152924352130 52:435
        634134152924362131 52:436
        634134152924372131 52:437
        634134152924382132 52:438
        634134152924392133 52:439
        634134152924402133 52:440
        634134152924412134 52:441
        634134152924422134 52:442
        634134152924432135 52:443
    

    【讨论】:

    • 他询问系统时间。秒表只是给出一个经过的时间。
    • 调用DateTime.Ticks 并不比DateTime.Millisecond 更准确。在内部,DateTime.Millisecond 调用 DateTime.Ticks
    • 对。我只是指出,根据 MSDN 和这个粗略的测试,它至少可以精确到提问者要求的毫秒分辨率;因此并排 Console.Output 的 DateTime 作为字符串旁边的 Ticks。
    猜你喜欢
    • 2016-06-22
    • 1970-01-01
    • 1970-01-01
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-20
    • 2020-03-18
    相关资源
    最近更新 更多