如何保证时间戳始终是唯一的?时间、是唯一

2023-09-02 11:55:51 作者:北巷长歌悠

我使用时间戳时间顺序在我的程序并发修改,并要求改变每个时间戳是唯一的。然而,我发现,简单地调用 DateTime.Now 是不够的,好象叫临门它通常会返回相同的值。

I'm using timestamps to temporally order concurrent changes in my program, and require that each timestamp of a change be unique. However, I've discovered that simply calling DateTime.Now is insufficient, as it will often return the same value if called in quick succession.

我有一些想法,但没有给我的印象最好的解决了这个。有没有我可以写这将保证每个连续调用产生一个独特的方法的DateTime

I have some thoughts, but nothing strikes me as the "best" solution to this. Is there a method I can write that will guarantee each successive call produces a unique DateTime?

如果我也许可以使用不同类型的这一点,也许一个长整型? 的DateTime 有被轻松地跨preTABLE作为一个实时的明显优势,不像,说,一个递增计数器。

Should I perhaps be using a different type for this, maybe a long int? DateTime has the obvious advantage of being easily interpretable as a real time, unlike, say, an incremental counter.

更新:这是我最后的编码作为一个简单的折衷办法,即仍允许我使用的DateTime 作为我的临时密钥,同时保证独特性每个调用方法时:

Update: Here's what I ended up coding as a simple compromise solution that still allows me to use DateTime as my temporal key, while ensuring uniqueness each time the method is called:

private static long _lastTime; // records the 64-bit tick value of the last time
private static object _timeLock = new object();

internal static DateTime GetCurrentTime() {
    lock ( _timeLock ) { // prevent concurrent access to ensure uniqueness
        DateTime result = DateTime.UtcNow;
        if ( result.Ticks <= _lastTime )
            result = new DateTime( _lastTime + 1 );
        _lastTime = result.Ticks;
        return result;
    }
}

由于每一个刻度值只是一个10万分之一秒,这种方法只介绍每秒(其中,顺便说一句,它是足够有效的执行),1000万次的顺序调用时显着时钟偏移,这意味着它为我的目的完全可以接受的。

Because each tick value is only one 10-millionth of a second, this method only introduces noticeable clock skew when called on the order of 10 million times per second (which, by the way, it is efficient enough to execute at), meaning it's perfectly acceptable for my purposes.

下面是一些测试code:

Here is some test code:

DateTime start = DateTime.UtcNow;
DateTime prev = Kernel.GetCurrentTime();
Debug.WriteLine( "Start time : " + start.TimeOfDay );
Debug.WriteLine( "Start value: " + prev.TimeOfDay );
for ( int i = 0; i < 10000000; i++ ) {
    var now = Kernel.GetCurrentTime();
    Debug.Assert( now > prev ); // no failures here!
    prev = now;
}
DateTime end = DateTime.UtcNow;
Debug.WriteLine( "End time:    " + end.TimeOfDay );
Debug.WriteLine( "End value:   " + prev.TimeOfDay );
Debug.WriteLine( "Skew:        " + ( prev - end ) );
Debug.WriteLine( "GetCurrentTime test completed in: " + ( end - start ) );

...和结果:

...and the results:

Start time:  15:44:07.3405024
Start value: 15:44:07.3405024
End time:    15:44:07.8355307
End value:   15:44:08.3417124
Skew:        00:00:00.5061817
GetCurrentTime test completed in: 00:00:00.4950283

因此​​,换句话说,在半秒它产生千万的独特的时间戳,和最终的结果是仅由半秒被推向前。在实际应用中的偏斜会不明显。

So in other words, in half a second it generated 10 million unique timestamps, and the final result was only pushed ahead by half a second. In real-world applications the skew would be unnoticeable.

推荐答案

获得时间戳的严格递增的序列,没有重复的一种方法是以下code。

One way to get a strictly ascending sequence of timestamps with no duplicates is the following code.

相比,这里的其他答案这其中有以下好处:

Compared to the other answers here this one has the following benefits:

的值跟踪紧密结合实际的实时值(除非在极端的情况下有很高的要求利率时,他们会得到略高于实时)。 这是锁定免费的,应该是使用锁定报表解决方案,更好的表现。 在它保证升序(只需附加一个循环计数器不)。 The values track closely with actual real-time values (except in extreme circumstances with very high request rates when they would get slightly ahead of real-time). It's lock free and should perform better that the solutions using lock statements. It guarantees ascending order (simply appending a looping a counter does not).

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

           return newValue;
       }
   }
}