【问题标题】:Thread lock allows simultaneous thread access线程锁允许同时线程访问
【发布时间】: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


【解决方案1】:

在您的代码中,看起来每个线程都在处理一个单独的函数实例,因此它们每个都锁定在自己的(单独的)锁定对象上。

快速解决方法是将该对象设为静态,以便在所有函数实例之间共享,或者让所有线程共享同一个函数实例。

【讨论】:

  • 谢谢 DaveF,解决了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多