我需要生成一个唯一的数字ID来附加到一个传入的请求。这个ID仅用于临时跟踪请求,并在请求完成处理后将被丢弃。这个ID只会在这个应用程序的上下文中使用,但需要以高性能多线程的方式分配。
我想使用DateTime.Now.Ticks来生成这个ID,但想知道如果同时处理并发请求,是否仍然会生成冲突的ID?
如果有人可以建议一个更好的方法来生成这些ID(最好不是像Tick一样的Int64),请告诉我。即使只是一个递增的数字也足够简单,只要我不必在递增之前锁定这个数字。
我需要生成一个唯一的数字ID来附加到一个传入的请求。这个ID仅用于临时跟踪请求,并在请求完成处理后将被丢弃。这个ID只会在这个应用程序的上下文中使用,但需要以高性能多线程的方式分配。
我想使用DateTime.Now.Ticks来生成这个ID,但想知道如果同时处理并发请求,是否仍然会生成冲突的ID?
如果有人可以建议一个更好的方法来生成这些ID(最好不是像Tick一样的Int64),请告诉我。即使只是一个递增的数字也足够简单,只要我不必在递增之前锁定这个数字。
你只需要使用一个静态变量,每次想要另一个唯一的值时就将其递增即可。你可以通过使用Interlocked.Increment方法使其线程安全且非常快速。
// Declaration
private static int safeInstanceCount = 0;
// Usage
{
...
Interlocked.Increment(ref safeInstanceCount);
...
}
time csharp <<< "int x; while (System.Threading.Interlocked.Increment(ref x)<1000000000);"
显示它大约每秒执行121,521,448次增量操作(比仅使用 x++
慢2.6倍)。因此(像往常一样),是否需要优化实际上取决于在那一秒钟内还需要完成什么其他任务。 - seheThreadStart a = () => { int x=0; while(Interlocked.Increment(ref x) < 1000000000); };
并行x4需要9秒;共享int x
(通过将其从lambda中取出)需要32秒(仅约3100万次增量/秒),而对Interlocked.Increment的总调用次数实际上减少了4倍 :) - seheDateTime.Now
并不适合这个目的。最好的情况下,你只能得到1毫秒的分辨率;最差的情况是在NT系统上需要17毫秒,在CE/Compact Framework上需要1秒钟!
考虑使用Interlocked.Increment
方法来实现快速且线程安全的计数器。
DateTime.Now
的实现具有这样的精度;我在帖子中提到了所有实现限制。如果您真的需要微秒级精度,Stopwatch
做得更好。 - Jeffrey HantinGuid
来确保我的临时文件夹 - 无论如何都会被清理干净 - 是唯一的。 - PeterX如果有多个线程发起请求,首先使用每个线程的ID,再加上每个线程的计数器(如果每个线程都需要发起多个请求)。
如果您知道将要使用多少线程(或至少有一个上限),则可以在线程之间分配ID空间,计算ID值为(线程本地)计数器的值和线程ID的组合 - 例如,counter_value++ << 8 | thread_id
。因此,不需要在线程之间进行协调或锁定,并且生成ID仅需要增量、位移和或运算。
如果您使用系统线程ID,则ID会稍微长一些,但是您不需要手动为线程分配ID。
我正在使用基于时间的简单生成ID,可能会有所帮助。
private static readonly string prefixNumber = ConfigManager.Current.GetAppSetting("AppTimeIdPrefix", "");
private static readonly DateTime epoch = DateTime.SpecifyKind(DateTime.Parse(ConfigManager.Current.GetAppSetting("AppTimeIdEpoch", "1970/01/01")), DateTimeKind.Utc);
/// <summary>
/// DateTime.Now is only updated every 10-15ms.
/// </summary>
private static long lastTime = CurrentTimeMilliseconds();
private static readonly object timeLock = new object();
private static long CurrentTimeMilliseconds()
{
return (long)(DateTime.UtcNow.ToUniversalTime() - epoch).TotalMilliseconds;
}
public static long CreateId()
{
lock (timeLock)
{ // prevent concurrent access to ensure uniqueness
long original, newValue;
do
{
original = lastTime;
newValue = Math.Max(CurrentTimeMilliseconds(), original + 1);
} while (Interlocked.CompareExchange(ref lastTime, newValue, original) != original);
return long.Parse(string.Format("{0}{1}", prefixNumber, newValue));
}
}
请参考如何确保时间戳始终唯一?
Interlocked
类会使使用Interlocked
的初衷失去意义(避免由锁引起的竞争)。 - Theodor Zoulias可以使用这个属性,但是需要支付1毫秒的时间,不过这并不重要!
public static long UniqId {
get {
Thread.Sleep(1);
return long.Parse(DateTime.Now.ToString("yyMMddHHmmssffff"));
}
}