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

标签 c# .net datetime concurrency

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

我有一些想法,但没有什么是解决这个问题的“最佳”解决方案。有没有我可以编写的方法来保证每次连续调用都产生唯一的 DateTime

我是否应该为此使用不同的类型,也许是 long int? DateTime 具有明显的优势,即可以轻松解释为实时时间,这与增量计数器不同。

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

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;
    }
}

因为每个 tick 值只有 1000 万分之一秒,所以此方法仅在以每秒 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 ) );

...以及结果:

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

也就是说,半秒内生成了1000万个unique时间戳,最后的结果只往前推了半秒。在实际应用中,这种偏差是不明显的。

最佳答案

获取严格升序的时间戳序列且不重复的一种方法是以下代码。

与此处的其他答案相比,此答案具有以下优点:

  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;
       }
   }
}

另请注意下面的注释,即应使用 original = Interlocked.Read(ref lastTimestamp);,因为 64 位读取操作在 32 位系统上不是原子的。

关于c# - 如何确保时间戳始终是唯一的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5608980/

相关文章:

MySQL 根据日期从多个表中获取结果

python - 如何在 Pandas 数据框中按行值对日期时间列进行排序?

c# - Linq groupby 列表匹配和计数

带有错误处理的 C# Task.WaitAll() 取消

c# - 对 ASP.Net Web 应用程序的 App_Code 进行单元测试

c# - 关于C#中线程的问题

c# - 基于圆弧的渐变

c# - 使用 JSON 格式正确配置 NLog 到 IHostBuilder

.net - 如何在 Linux 中从 .NET Core 2.0 创建可执行控制台应用程序?

C# Datetime 短年份与国家公约