c# - 需要改进的线程锁定建议

标签 c# multithreading locking

我有以下场景:

我正在尝试锁定一个线程,如果该线程的“自定义”id 与已经进入代码锁定部分的线程匹配,但如果 id 不同则不会。

我创建了一些示例代码来解释我想要的行为

class A
{        
    private static Dictionary<int, object> _idLocks = new Dictionary<int, object>();
    private static readonly object _DictionaryLock = new object();
    private int _id;

    private void A (int id)
    {
        _id = id;
    }

    private object getObject()
    {
        lock (_DictionaryLock)
        {
            if (!_idLocks.ContainsKey(_id))
                _idLocks.Add(_id, new object());
        }
        lock (_idLocks[_id])
        {
            if (TestObject.Exists(_id))
                return TestObject(_id);
            else
                return CreateTestObject(_id);
        }
    }
}

现在这对我扩展的内容 100% 有效,其中 id 示例 1 不检查其对象是否已创建,而另一个 id 为 1 的线程已经忙于创建该对象。

但是拥有两个锁和一个静态字典似乎根本不是正确的做法,所以我希望有人能向我展示一种改进的方法来阻止线程访问代码,前提是该线程是用相同的方式创建的id 为已经忙于执行锁定部分中的代码的人。

我正在查看 ReaderWriterLockSlim 类,但对我来说使用它并没有真正意义,因为我根本不希望在对象 TestObject(id) 仍在创建时读取它。

我不关心锁定线程访问字典。 我试图不惜一切代价避免的是该线程运行的 _id 不应在 CreateTestObject(_id) 内部使用,而已经有一个忙,因为正在使用该 id 创建和删除文件如果两个线程试图访问相同的文件,这将抛出异常

这可以用普通锁修复,但在这种情况下,我仍然希望 _id 当前未在 CreateTestObject(_id) 方法中运行的线程能够在锁。

这都是因为 CreateTestObject 内部发生的事情需要时间,如果线程正在等待访问它,性能将会受到影响。

最佳答案

看起来您正在使用此代码以线程安全的方式填充字典 - 您可以使用 ConcurrentDictionary 吗?相反?

class A {
  private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>();

  private int _id;

  private object GetObject() {
    object output = null;
    if(_dictionary.TryGetValue(_id, output)) {
      return output;
    } else {
      return _dictionary.GetOrAdd(_id, CreateTestObject(_id));
    }
  }
}

编辑:如果你想完全消除调用重复 CreateTestObject 方法的可能性,那么你可以在 _dictionary 中存储一个包装器,它延迟设置 object

class Wrapper {
  private volatile object _obj = null;

  public object GetObj() {
    while(_obj == null) {
      // spin, or sleep, or whatever
    }
    return _obj;
  }

  public void SetObj(object obj) {
    _obj = obj;
  } 
}

class A {
  private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>();

  private int _id;

  private object GetObject() {
    Wrapper wrapper = null;
    if(_dictionary.TryGetValue(_id, wrapper)) {
      return wrapper.GetObj();
    } else {
      Wrapper newWrapper = new Wrapper();
      wrapper = _dictionary.GetOrAdd(_id, newWrapper);
      if(wrapper == newWrapper) {
        wrapper.SetObj(CreateTestObject(_id));
      }
      return wrapper.GetObj();
    }
  }
}

只有一个线程能够将新的 Wrapper 放入 _dictionary 中指定的 _id - 该线程将初始化其中的对象wrapper == newWrapper 条件。 Wrapper#GetObj 旋转直到设置对象,这可以重写为 block 。

关于c# - 需要改进的线程锁定建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30508609/

相关文章:

c# - 泛型函数重载

c# - 设置 Backgroundworker MVVM,更新 Progressbar

c# - 锁定字段或局部变量?

python - 在 Windows 上以非侵入方式解锁文件

sql-server - SQL Server 中默认的锁定粒度是多少?

c# - 如何使用 protobuf-net 进行 xml 序列化?

c# - 为什么 C# 编译器使用无效方法的重载?

c# - 将 Between 和其他函数作为 LINQ 表达式实现

c - 在 Pthread 库中杀死线程

c - fgets() 是否锁定 stdout 以防止 printf