我正在使用时间戳对程序中的并发更改进行临时排序,并要求更改的每个时间戳都是唯一的。但是,我发现仅调用 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时间戳,最后的结果只往前推了半秒。在实际应用中,这种偏差是不明显的。
最佳答案
获取严格升序的时间戳序列且不重复的一种方法是以下代码。
与此处的其他答案相比,此答案具有以下优点:
- 这些值与实际实时值密切相关(除非在请求率非常高的极端情况下,它们会稍微领先于实时值)。
- 它是无锁的,应该比使用
lock
语句的解决方案表现更好。 - 它保证升序(简单地附加一个循环计数器不能保证)。
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/