Windows系统时间精确到毫秒

7

与我之前的问题相关,但是使用C#,我需要精确到毫秒的系统时间。

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

队列性能计数器也是如此。是否有其他方法可以获得精确的毫秒级精度?

3个回答

10

Windows为了节省电力,不会每秒更新系统时钟1000次,而是默认只更新60-100次每秒。如果你将多媒体计时器设置为1毫秒,你可以从时钟中获得1毫秒的分辨率,但这并不推荐。

更进一步说,当CPU处于空闲状态一段时间时,它可以进入一个非常低功耗状态。每当它被中断(例如增加时钟滴答数)时,它必须离开其非常低功耗状态,并使用大量电力来唤醒整个CPU以服务该中断。换句话说,额外的能量不在于增加时钟滴答数,而是在于使CPU保持唤醒状态来执行此操作。

由于我的笔记本电脑在时钟频率为60Hz时使用10瓦特,在时钟频率为1000Hz时使用11瓦特,我可以获得300分钟的电池续航时间,因此较慢的时钟为我提供了将近30分钟的额外电池续航时间!


Nitrodist:是的,那就是同一个问题的解释。 - Gabe
他正在谈论内核滴答声,内核滴答声是一种定时硬件(CPU)“计时器中断”。尽管Windows在电力效率方面远不如Android(低50%)和Mac OS(低20%),但这并不是让它变得更糟的理由。内核滴答声用于调用已过期(或合并)的计时器回调、重新更新线程/进程调度以及增加全局操作系统滴答声计数。 - v.oddou
这个回答如何解决问题?你解释了为什么DateTime.Now不够准确,但是你没有展示如何提高精度... - Everyone

8

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

更新
CodeProject链接已失效。我已从archive.org 提取了代码并将其嵌入此处以供参考。该代码按照CodeProject页面上的方式“原样”包含在此处。

DateTimePreciseDateTime.Now一样易于使用,只是DateTimePrecise.Now是一个实例方法而不是静态方法,因此您必须首先实例化一个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 Herbert
在使用此代码时要小心,它会因试图除以零而抛出异常。 - Dan H
@Dan H:你能举个抛出异常的例子吗? - Răzvan Flavius Panda
我能使用这个类来比较两个不同应用程序的时间吗?也就是说,我不仅需要微秒精度,还需要微秒准确性。 - Oleg Vazhnev
@javapowered 时间应该是*相当准确的。该类使用Date.UtcNow作为其基础,然后使用系统的高精度计时器计算自UtcNow以来的时间。因为它是从DateTime.UtcNow派生出来的,所以它永远不可能比那更准确。您还应该检查您的硬件的Stopwatch.Frequency以确定高精度功能是否实际工作。 - Dan Herbert

6

尝试使用System.Diagnostics.Stopwatch进行高分辨率计时。

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

尝试使用本机DateTime.Ticks,可实现系统时间精度高达100纳秒;1毫秒= 10000个ticks。

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

3
他要求获取系统时间。秒表只提供经过的时间。 - Gabe
3
调用 DateTime.Ticks 并不能比调用 DateTime.Millisecond 更准确。实际上,DateTime.Millisecond 内部也是通过调用 DateTime.Ticks 实现的。 - Dan Herbert
没错。我只是指出根据MSDN和这个粗略的测试,它至少以毫秒分辨率准确无误地回答了提问者的问题;因此将DateTime作为字符串与Ticks并排输出。 - xcud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接