直到现在我用 DateTime.Now 为获得有时间戳,但我注意到,如果你打印 DateTime.Now 在一个循环,你将看到它增量在descrete跳约。15秒。但对于某些场景在我的应用程序,我需要获得最准确时间戳记可能的话,最好与蜱(=100ns)的精确度。任何想法?

更新:

显然, StopWatch / QueryPerformanceCounter 是的路要走,但是它只能被用于测量时间,所以我想叫 DateTime.Now 当的应用程序的开始和然后就有 StopWatch 运行,然后只需增加经过的时间从 StopWatch 初始价值的返回 DateTime.Now.至少应该给我准确的相对时间戳,对吗?你怎么想,(砍)?

注:

StopWatch.ElapsedTicks 是不同的 StopWatch.Elapsed.Ticks!我使用了以前的假设1勾=100ns,但在这种情况下1勾=1/ StopWatch.Frequency.所以要得到刻度相当于时间使用 StopWatch.Elapsed.Ticks.我刚刚得知这一困难的方式。

注2:

使用的秒表的方法,我注意到了这一点得到了与实际时间。之后约10个小时,这是通过提前5秒钟。所以我猜一个就必须重新同步,它每X或所以在X可能1小时30分钟,15分钟,等等。我不知道什么是最佳的时间跨度为重,将由于每一步将改变抵消,它可能高达20秒。

有帮助吗?

解决方案

DateTime.Now 读取的系统时钟的值仅每15 ms左右更新一次(或在某些系统上为10 ms),这就是围绕这些时间间隔量化时间的原因。由于您的代码在多线程操作系统中运行,因此存在额外的量化效果,因此存在应用程序不是“活动”的延伸。因此不会测量实际当前时间。

由于您正在寻找超精确的时间戳值(而不是仅计时任意持续时间), Stopwatch 类本身将无法满足您的需求。我想你必须自己使用一种 DateTime / 秒表混合。当您的应用程序启动时,您将存储当前的 DateTime.UtcNow 值(即应用程序启动时的原始分辨率时间),然后还启动 Stopwatch 对象,如下所示:

DateTime _starttime = DateTime.UtcNow;
Stopwatch _stopwatch = Stopwatch.StartNew();

然后,只要您需要高分辨率的 DateTime 值,就可以得到这样的结果:

DateTime highresDT = _starttime.AddTicks(_stopwatch.Elapsed.Ticks);

你也可能想要定期重置_starttime和_stopwatch,以防止结果时间与系统时间过于不同步(虽然我不确定这实际上是否会发生,而且需要很长时间才能完成无论如何都会发生。)

更新:因为看起来秒表 与系统时间不一致(每小时多半秒),我认为这是有意义的根据调用之间传递的时间来重置混合 DateTime 类以检查时间:

public class HiResDateTime
{
    private static DateTime _startTime;
    private static Stopwatch _stopWatch = null;
    private static TimeSpan _maxIdle = 
        TimeSpan.FromSeconds(10);

    public static DateTime UtcNow
    {
        get
        {
            if ((_stopWatch == null) || 
                (_startTime.Add(_maxIdle) < DateTime.UtcNow))
            {
                Reset();
            }
            return _startTime.AddTicks(_stopWatch.Elapsed.Ticks);
        }
    }

    private static void Reset()
    {
        _startTime = DateTime.UtcNow;
        _stopWatch = Stopwatch.StartNew();
    }
}

如果您定期重置混合计时器(例如每小时或某些时间),则可能会在最后一次读取时间之前设置时间,就像微型夏令时问题一样。

其他提示

要获得高分辨率的滴答计数,请使用静态Stopwatch.GetTimestamp() - 方法:

long tickCount = System.Diagnostics.Stopwatch.GetTimestamp();
DateTime highResDateTime = new DateTime(tickCount);

只需看一下.NET源代码:

    public static long GetTimestamp() {
        if(IsHighResolution) {
            long timestamp = 0;    
            SafeNativeMethods.QueryPerformanceCounter(out timestamp);
            return timestamp;
        }
        else {
            return DateTime.UtcNow.Ticks;
        }   
    }

源代码: http:// referencesource .microsoft.com /#系统/服务/监测/系统/ diagnosticts / Stopwatch.cs,69c6c3137e12dab4

[接受的答案似乎不是线程安全的,并且它自己的录取可能会及时倒退导致重复的时间戳,因此这个替代答案]

如果您真正关心的(根据您的评论)实际上是一个以严格升序分配并且尽可能与系统时间尽可能相对应的唯一时间戳,您可以尝试这种替代方法:

public class HiResDateTime
{
   private static long lastTimeStamp = DateTime.UtcNow.Ticks;
   public static long UtcNowTicks
   {
       get
       {
           long orig, newval;
           do
           {
               orig = lastTimeStamp;
               long now = DateTime.UtcNow.Ticks;
               newval = Math.Max(now, orig + 1);
           } while (Interlocked.CompareExchange
                        (ref lastTimeStamp, newval, orig) != orig);

           return newval;
       }
   }
}

这些建议看起来都太难了!如果您使用的是Windows 8或Server 2012或更高版本,请使用 GetSystemTimePreciseAsFileTime ,如下所示:

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern void GetSystemTimePreciseAsFileTime(out long filetime);

public DateTimeOffset GetNow()
{
    long fileTime;
    GetSystemTimePreciseAsFileTime(out fileTime);
    return DateTimeOffset.FromFileTime(fileTime);
}

这比 DateTime.Now 更精确,准确无误。

有关详细信息,请参阅MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v = vs.85)的.aspx

它确实返回操作系统已知的最准确的日期和时间。

操作系统还通过 QueryPerformanceCounter QueryPerformanceFrequency (.NET 秒表类)提供更高分辨率的时序。这些让你计时间隔,但不给你一天的日期和时间。你可能会争辩说,这些可以给你一个非常准确的时间和日期,但我不确定他们在很长一段时间内有多么糟糕。

1)。如果您需要高分辨率绝对精度:您不能使用 DateTime.Now 当它基于间隔为15毫秒的时钟时(除非它 是可能的“滑动”阶段)。

相反,绝对更好的分辨率的外部来源 下面的准确时间(例如ntp), t1 可以与高组合 分辨率计时器(StopWatch / QueryPerformanceCounter)。


2)。如果你只需要高分辨率:

示例 DateTime.Now t1 )一次与来自的值 高分辨率计时器(StopWatch / QueryPerformanceCounter) (<码> TT0 )。

如果高分辨率计时器的当前值是 tt 那么 当前时间 t ,是:

t = t1 + (tt - tt0)

3)。另一种选择可能是解开绝对时间和 财务事件的顺序:绝对时间的一个值 (15分钟的分辨率,可能会在几分钟后关闭)和一个 订单的值(例如,将值递增1 时间和商店)。订单的起始值可以是 基于某些系统的全局值或从中得出 应用程序启动的绝对时间。

此解决方案将更加强大,因为它不依赖于此 时钟/定时器的底层硬件实现 (可能因系统而异)。

这对于如此简单的事情来说太过分了。只需在交易中插入数据库中的DateTime即可。然后要获得交易订单,请使用您的标识列,该列应该是递增值。

如果您插入多个数据库并尝试在事后协调,那么由于数据库时间的任何轻微差异(即使按照您所说的ns增量),您将错误计算交易订单

要解决多个数据库问题,您可以公开每个交易点击以获得订单的单个服务。该服务可以返回DateTime.UtcNow.Ticks和递增的交易号。

即使使用上述解决方案之一,任何从网络上的某个位置进行交易且延迟更多的人都可能首先进行交易(真实世界),但由于延迟,它们会以错误的顺序进入。因此,必须考虑将交易放在数据库中,而不是放在用户的控制台上。

15毫秒(实际上可以是15-25毫秒)的准确度基于Windows 55&nbsp; Hz / 65&nbsp; Hz定时器分辨率。这也是TimeSlice的基本时期。受影响的是 Windows.Forms.Timer Threading.Thread.Sleep Threading.Timer 等。

要获得准确的间隔,您应该使用秒表课。它将使用高分辨率(如果可用)。请尝试以下语句来了解:

Console.WriteLine("H = {0}", System.Diagnostics.Stopwatch.IsHighResolution);
Console.WriteLine("F = {0}", System.Diagnostics.Stopwatch.Frequency);
Console.WriteLine("R = {0}", 1.0 /System.Diagnostics.Stopwatch.Frequency);

我得到R = 6E-08秒,或60 ns。它应该足以满足你的目的。

我想补充以下有关 MusiGenesis 答案 重新同步的时机。

意义:什么时候我应该使用重新同步(该 _maxIdleMusiGenesis 答案)

你知道,这个解决方案都不完全准确的,这就是为什么你重新同步。但还有什么你隐含的想要的是同样的事情 Ian Mercer 解决方案:

唯一的时间戳,被分配在严格的上升顺序

因此量的时间之间的两重新同步( _maxIdle 让我们叫它 SyncTime)应功能的4件事:

  • DateTime.UtcNow 决议
  • 比你想的精确度
  • 精确水平,你想要的
  • 估计的不同步的比你的机器

显然,第一约束在这个可变将是:

不同步的比例 <=准确性比例

例如:我不想我的准确是最糟糕的比0.5s/小时或1毫秒/天等等...(在不良文:我不想要比0.5s/小时=12/天)。

所以你不可能实现一个更好的准确性比什么秒表提供你在你的电脑。这取决于你的不同步的比率,这可能不是一定的。

另一个制约因素是最短的时间之间的两重新同步:

Synctime>= DateTime.UtcNow 决议

在这里,精确度和精确度都相联系,因为如果你使用一个高精密度(例如储存在数据库),但一个较低的准确性,你可能会破坏 Ian Mercer的声明 这是严格的上升顺序。

注: 它似乎DateTime。UtcNow可能有一个更大的默认Res于15毫秒(1毫秒在我的机器) 按照链接:高精确度的日期时间。UtcNow

让我们采取一个例子:

想象不同步的比例的评论上。

之后约10个小时,这是通过提前5秒钟。

说我要一μs的精确度。我的计时器res1毫秒 (参见上文注)

这么点点:

  • DateTime.UtcNow 决议: 1毫秒
  • 准确性的比例>=同步的比例, 可以采取的最准确可能如此: 准确性的比率=同步的比例
  • 精确水平,你想: 1μs
  • 估计的不同步的比你的机器: 0.5s/小时 (这也是我的精度)

如果你重每10,想象一下,你在9.999s,1毫秒之前重置。在这里,你打个电话,在此时间间隔。的时候你的功能将情节是领先的:0.5/3600*9.999s eq1.39ms。你会显示一段时间的10.000390秒。后UtcNow打勾,如果你打电话内390micro秒,你将有一个数字低于前一个。它更糟糕的是,如果这个同步的比例是随机的根据CPU负载或其他的东西。

现在,让我们说我把 SyncTime 在其最小值>我重新同步,每1毫秒

这样做的同样的想法会把我的时间提前通过0.139μs < 低劣的精我想要的。因此,如果我电话的功能在9.999ms,使1microsec前重置的,我将积9.999.只是之后我将阴谋10.000.我会有一个良好的秩序。

因此,这里的其他约束是:准确性的比例x SyncTime < 精确水平,可以说是肯定的,因为数量可以是圆的, 准确性的比例x SyncTime < 精度水平/2 是好的。

问题得到解决。

所以快速回顾一下就是:

  • 找回你的计时器的决议。
  • 计算的估计的不同步的比例。
  • 准确性的比例>=同步的比例估计, 最好的精确度=同步的比例
  • 选择你的精准水平考虑到如下:
  • 计时器的决议 <=SyncTime <=PrecisionLevel/(2*精确性比)
  • 最好的精度可以实现的是 计时器-分辨率*2*同步的比例

对于上述比例(0.5/小时)正确SyncTime将是3.6毫秒,使用四舍五入到 3ms.

与上述比例和定时器的决议1毫秒.如果你想要一个 一个-打勾精度水平 (0.1μs),需要一个同步的比率不超过: 180ms/小时。

在其最后答复对其自己的答案 MusiGenesis 国家:

@赫尔曼:我一直在运行一个类似的试验为后两个小时(而不复修正),并表基础的计时器是唯一运行约400ms前后2小时,所以斜本身看来是变量(但仍然很严重)。我很惊讶的是,倾斜是这个坏;我猜这就是为什么秒表在系统。诊断。–MusiGenesis

因此秒表的准确度接近200毫秒/小时,几乎我们的180ms/小时。是否有任何链接,为什么我们的数量,这个数字是如此的接近?不知道。但是这种精确度是足够我们达到刻度精密

最好的PrecisionLevel:对于上述例子也是 0.27μs.

但是什么会发生,如果我呼叫它多次之间9.999ms和重新同步。

2电话的功能可以最终用相同的时间戳正在返回时将9.999为(作为我不要看到更多的精度).为了避开这,你不能触摸的精确水平,因为它与SyncTime通过上述的关系。所以你应该实现 Ian Mercer 解决方案是为那些情况。

请不要犹豫评论我的答案。

如果需要时间戳来执行基准测试,请使用 StopWatch 具有比DateTime.Now。

更好的精度
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top