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

我正在使用时间戳来临时命令我的程序中的并发更改,并要求更改的每个时间戳都是唯一的。 但是,我发现简单地调用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; } } 

因为每个滴答值只有一千万分之一秒,所以这种方法在每秒调用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  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万个独特的时间戳,最终结果只推迟了半秒钟。 在实际应用中,偏差将是不明显的。

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

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

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

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

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

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

本身并不是骗局,但这个主题有一些关于减少重复/提供更好的时序分辨率的想法:

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

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

我发现最简单的方法是组合时间戳和primefaces计数器。 您已经知道时间戳分辨率较差的问题。 使用primefaces计数器本身也有一个简单的问题,即如果要停止并启动应用程序则需要存储其状态(否则计数器将从0开始,导致重复)。

如果您只是寻找一个唯一的id,那么就像将时间戳和计数器值与之间的分隔符连接起来一样简单。 但是因为你希望值总是按顺序排列,这是不够的。 基本上您需要做的就是使用primefaces计数器值为您的时间戳添加额外的固定宽度精度。 我是一名Java开发人员,所以我还不能提供C#示例代码,但两个领域的问题都是一样的。 所以请按照以下一般步骤操作:

  1. 您将需要一种方法来为您提供从0-99999循环的计数器值。 100000是将毫秒精度时间戳与64位长的固定宽度值连接时可能的最大值。 因此,您基本上假设在单个时间戳分辨率(15ms左右)内永远不会需要超过100000个ID。 一个静态方法,使用Interlocked类提供primefaces递增并重置为0是理想的方法。
  2. 现在要生成您的ID,您只需将时间戳与填充为5个字符的计数器值连接起来。 因此,如果您的时间戳是13023991070123而您的计数器是234,那么ID将是1302399107012300234。

只要您不需要比每秒6666更快的ID(假设15ms是您最精细的分辨率),此策略将起作用,并且无需在应用程序的重新启动期间保存任何状态。

它不能保证是唯一的,但也许使用刻度线足够精细?

单个刻度表示一百纳秒或一千万分之一秒。 一毫秒内有10,000个刻度。

不确定你要完全做什么,但可能会考虑使用队列来处理顺序进程记录。