锁定一个非线程安全的对象,这是可以接受的做法吗?

Mik*_*rds 4 c# multithreading locking thread-safety

我在前几天发表的评论中对此感到悲痛,所以我想发布这个问题,试图让人们告诉我,我疯了,我会接受,或者告诉我,我可能是对的,我也很乐意接受.我也可以接受两者之间的任何事情.

假设您有一个非线程安全的对象类型,例如Dictionary<int, string>.为了论证,我知道你也可以使用ConcurrentDictionary<int, string>哪个是线程安全的,但我想谈谈在多线程环境中围绕非线程安全对象的一般做法.

请考虑以下示例:

private static readonly Dictionary<int, string> SomeDictionary = new Dictionary<int, string>();
private static readonly object LockObj = new object();

public static string GetById(int id)
{
  string result;

  /** Lock Bypass **/
  if (SomeDictionary.TryGetValue(id, out result)
  {
    return result;
  }

  lock (LockObj)
  {
    if (SomeDictionary.TryGetValue(id, out result)
    {
      return result;
    }

    SomeDictionary.Add(id, result = GetSomeString());
  }

  return result;
}
Run Code Online (Sandbox Code Playgroud)

锁定模式称为Double-Checked Locking,因为如果已使用该id初始化字典,则会主动绕过锁定.在锁中调用字典的"添加"方法,因为我们只想调用该方法一次,因为如果您尝试添加具有相同键的项,它将引发异常.

我的理解是,这种锁定模式基本上同步了Dictionary的处理方式,这使它成为线程安全的.但是,我得到了一些负面评论,说明它实际上是如何使其线程安全的.

所以,我的问题是,在多线程环境中非线程安全对象的锁定模式是否可以接受?如果没有,那么什么是更好的模式?(假设没有相同的C#类型是线程安全的)

Jon*_*eet 8

不,这不是安全的.该TryGetValue方法不是线程安全的,因此当多个线程之间共享对象而不进行锁定时,不应使用它.双重检查锁定模式仅涉及测试引用 - 虽然不能保证提供最新结果,但不会引起任何其他问题.比较TryGetValue可以做任何事情(例如抛出异常,破坏内部数据结构),如果同时调用,比如说Add.

就个人而言,我只是使用锁,但你可能会使用ReaderWriterLockSlim.(在大多数情况下,简单锁定会更有效 - 但这取决于读取和写入操作需要多长时间,以及争用的内容.)