【发布时间】:2022-01-16 12:45:31
【问题描述】:
我有一个助手类,我的 webapp 的多个同时用户会经常调用它。此类中的一个函数尝试创建一个保证在数据库中唯一的字符串,即使 50 个不同的用户/线程同时调用该函数。以下是我的代码的基本部分:
private Object threadLock = new Object();
private string generateConfigIDThreadLock(Settings settings, string salesOrderNumber, string configModifiedOn) {
lock(threadLock) {
return generateConfigID(settings, salesOrderNumber, configModifiedOn);
}
}
private string generateConfigID(Settings settings, string salesOrderNumber, string configModifiedOn) {
string soTruncated = string.Empty;
if (salesOrderNumber.Length >= 5) {
soTruncated = salesOrderNumber.Substring(salesOrderNumber.Length - 5, 5);
} else {
soTruncated = salesOrderNumber;
}
int configModifiedOnSummed = Utilities.SumNumbers(configModifiedOn);
string newConfigID = $ "{soTruncated}-{DateTime.Now.Hour}{DateTime.Now.Minute}{DateTime.Now.Second}{DateTime.Now.Millisecond}-{configModifiedOnSummed}";
System.Threading.Thread.Sleep(50); // simulate the time for an API roundtrip
return newConfigID;
}
在我的测试工具中,我通过这样做来模拟大量同时使用:
static void Main(string[] args) {
int n = 50;
for (int i = 0; i < n; i++) {
Thread t = new Thread(TestOnMultipleThreads);
t.Start();
}
Console.ReadLine();
}
private static void TestOnMultipleThreads() {
var functions = new Functions();
string configID = functions.GetConfigID(string.Empty, "S-00018437", "2021-12-11 22:11:22");
if (configIDs.Contains(configID)) {
Console.WriteLine("PROLBEM! ConfigId was generated twice: " + configID);
} else {
Console.WriteLine("Unique configID: " + configID);
configIDs.Add(configID);
}
}
我不能使用 guid 来实现唯一性;我要求将字符串保持在 25 个字符以下,并在配置 ID 中使用 SO # 和时间戳的部分。注意TestOnMultipleThreads() 中硬编码的销售订单号和日期/时间戳;这是一个真实的场景。许多呼叫者可能具有相同的 SO # 和相同的日期时间。我需要使用销售订单# 的元素和当前时间戳来生成唯一性。如您所见,milsecs 是我唯一字符串的一部分,因此即使两次调用相隔 1 毫秒,生成的字符串 ID 也是唯一的。但在我的模拟中,我每次都会得到重复。这似乎意味着我的线程锁不起作用;如果是这样,并且由于我也在使用Thread.Sleep(50),那么配置 ID 生成不可能在相同的 1 毫秒内发生。
我做错了什么?为什么我得到重复?为什么没有按预期发生线程阻塞?
【问题讨论】:
-
什么是
functions.GetConfigID? -
除了下面的答案提到的错误之外,您可以使用 ID 的整数后缀使其唯一,您可以使用
Interlocked.Increment()递增。这样您就可以完全避免锁定。 -
如果限制为 25 个字符,请创建一个加密唯一的 100 位数字并将其编码为十六进制字符串。它的随机性比第 4 版 GUID(具有 IIRC,123 位熵)要少一些,但它可能已经足够好了。如果您使用 Base64 编码,您可能会获得 GUIDish 级别的熵。请记住,版本 4 GUID 的唯一性来自两个 足够随机 数字相同的可能性几乎为零
标签: c# multithreading thread-safety