我需要生成一个唯一的数字 ID 附加到传入请求。该 ID 仅临时用于跟踪请求,一旦请求完成处理,该 ID 将被丢弃。该 ID 仅在此应用程序的上下文中使用,但需要以高性能多线程方式分配。
我正在考虑使用 DateTime.Now.Ticks 作为此 ID,但想知道如果同时处理并发请求,DateTime.Now.Ticks 是否仍然可以生成冲突 ID?
如果有人可以建议一种在多线程环境中生成这些 ID 的更好方法(最好不是像 Tick 这样的 Int64 的方法),请告诉我。只要我不必在递增之前锁定数字,像递增数字这样简单的东西就足够了。
您只需要使用一个静态变量,每次您需要另一个唯一值时该变量就会递增。您可以使用 Interlocked.Increment 方法使该线程安全并且仍然非常快...
// Declaration
private static int safeInstanceCount = 0;
// Usage
{
...
Interlocked.Increment(ref safeInstanceCount);
...
}
DateTime.Now
对于这个目的来说绝对是糟糕的。最好的情况下,您的分辨率为 1 毫秒;最坏的情况下,NT 为 17 毫秒,CE/Compact Framework 为 1 秒(!)。
Interlocked.Increment
方法来实现快速、线程安全的计数器。
从每线程 ID 开始(如果多个线程发起请求),与每线程计数器连接(如果每个线程预计发起多个请求)。
只需获取一个强随机数或使用 GUID
http://msdn.microsoft.com/en-us/library/system.guid.newguid.aspx
如果高性能是必须具备的特性,请以单调的顺序分配连续的数字。通过为每个处理消息的线程“保留”一系列(例如 20-100)ID 来防止锁争用。这样,您只需在 20-100 次迭代中锁定序列生成器一次。
如果您知道将拥有多少个线程(或至少有一个上限),则可以在线程之间划分 ID 空间,将 ID 计算为(线程本地)计数器的值和线程的 ID - 例如,
counter_value++ << 8 | thread_id
。因此,线程之间不需要协调或锁定,生成 ID 仅需要增量、位移和或。
如果为此使用系统线程 ID,您的 ID 会稍微长一些,但您不需要手动为线程分配 ID。
你可以使用这样的东西。这是线程安全的、快速的,并且即使应用程序重新启动,也永远不会生成相同的数字,因为它与日期时间连接。
private static int safeInstanceCount = 0;
public static string GenerateUniqueId()
{
Interlocked.Increment(ref safeInstanceCount);
var uniqueNumber = long.Parse(DateTime.Now.ToString("yyMMddHHmmss"));
return $"AnyPreffix-{uniqueNumber}{safeInstanceCount}";
}
尝试类似:
using System;
using System.Threading;
public class HelloWorld
{
private static int safeInstanceCount = 0;
public static void Main(string[] args)
{
long uniqueNumber;
string str;
for(int a=0;a<15;a++) {
Interlocked.Increment(ref safeInstanceCount);
uniqueNumber = long.Parse(DateTime.Now.ToString("yyMMddHHmmss"));
str = $"ENR-{uniqueNumber}{safeInstanceCount}";
Console.WriteLine (str);
}
}
}
上述代码的结果将是:
或者另一个例子可以是:
using System;
using System.Threading;
using System.Linq;
public class HelloWorld
{
private static int safeInstanceCount = 0;
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
public static void Main(string[] args)
{
string uniqueNumber;
string str;
for(int a=0;a<15;a++) {
Interlocked.Increment(ref safeInstanceCount);
uniqueNumber = RandomString(4);
str = $"ENR-{uniqueNumber}{safeInstanceCount}";
Console.WriteLine (str);
}
}
}
但是这个例子有可能创建重复的值,如果 应用程序重新启动,因为它将重置静态变量 “safeInstanceCount”回到 0。
您还可以在这里测试代码: https://www.programiz.com/csharp-programming/online-compiler/
可以使用此属性,但要支付 1ms,这并不重要!
public static long UniqId {
get {
Thread.Sleep(1);
return long.Parse(DateTime.Now.ToString("yyMMddHHmmssffff"));
}
}
我正在使用基于时间的简单生成 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));
}
}