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

dev*_*os1 30 .net c# concurrency datetime

我正在使用时间戳来临时命令我的程序中的并发更改,并要求更改的每个时间戳都是唯一的.但是,我发现简单地调用DateTime.Now是不够的,因为如果快速连续调用,它通常会返回相同的值.

我有一些想法,但没有任何事情让我觉得这是"最好的"解决方案.有没有一种方法可以保证每次连续调用都能产生一个独特的DateTime

我应该为此使用不同的类型,也许是长整数?DateTime具有明显的优势,可以像实时一样轻松解释,不像增量计数器.

更新:这是我最终编写的一个简单的折衷解决方案,它仍然允许我DateTime用作我的临时密钥,同时在每次调用方法时确保唯一性:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

因为每个滴答值只有一千万分之一秒,所以这种方法在每秒调用1000万次时会引入明显的时钟偏差(顺便说一句,它的执行效率足够高),这意味着它是完全可以接受我的目的.

这是一些测试代码:

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 ) );
Run Code Online (Sandbox Code Playgroud)

......结果:

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
Run Code Online (Sandbox Code Playgroud)

换句话说,在半秒钟内,它产生了1000万个独特的时间戳,最终结果只推迟了半秒钟.在实际应用中,偏差将是不明显的.

Ian*_*cer 69

获得严格上升的时间戳序列而没有重复的一种方法是以下代码.

与其他答案相比,这个答案有以下好处:

  1. 这些值与实际实时值密切相关(除非在极端情况下具有非常高的请求率,否则它们将略微超前于实时).
  2. 它是无锁的,并且应该使用lock语句更好地执行解决方案.
  3. 它保证升序(简单地附加循环计数器不会).

.

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;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 适当使用`Interlocked.CompareExchange`的+1 (3认同)
  • +1.我针对此代码运行了一些单元测试.它确实有效,并且执行速度比任何其他解决方案都快.唯一的缺点是它对普通读者来说并不直观.我会花更多的时间向别人解释它对别人的影响,而不是简单的锁定(这很快).[复杂性/性能权衡].但是,如果可以的话,我会再把你的解决方案投票给更高的. (2认同)
  • 请记住,系统时间可能会有点跳动,即当它从时间服务器获得更新时。因此,如果时钟向后跳,您将得到一个与上一个值不同的值,但是当时钟赶上时,您可能会再次捕获与之前相同的值,而您的算法将不知道这一点。 (2认同)

Dom*_*nic 8

呃,你的问题的答案是"你不能",因为如果两个操作同时发生(它们将在多核处理器中),它们将具有相同的时间戳,无论你设置的精度如何收集.

也就是说,听起来你想要的是某种自动递增的线程安全计数器.要实现这一点(可能是一个全局服务,也许是在静态类中),你可以使用这个Interlocked.Increment方法,如果你认为你需要的不仅仅是int.MaxValue可能的版本Interlocked.Read.


Joe*_*Joe 6

DateTime.Now仅每10-15ms更新一次.

不是欺骗本身,但这个线程有一些减少重复/提供更好的时间分辨率的想法:

如何在.NET/C#中获取tick精度的时间戳?

话虽这么说:时间戳是信息的可怕关键; 如果事情快速发生,您可能需要一个索引/计数器,以便在项目发生时保持项目的离散顺序.那里没有歧义.