ConcurrentDictionary 中项目的 C# 线程安全原子操作

C# thread-safe atomic operation on item in ConcurrentDictionary

如何对其键可能在也可能不在 ConcurrentDictionary 中的项目执行原子操作?

例如,假设我想重置具有特定键的项目的一些状态值,它可能在也可能不在字典中。我不想重置所有状态值,所以简单地用新实例替换原始项目不是一个选项。

这是一个与我的用例非常相似的人为示例。您可以假设 类 & 方法包含更多不会影响手头问题的代码。

public class MyManager
{
    private ConcurrentDictionary<int, MyWorker> _dict
        = new ConcurrentDictionary<int, MyWorker>();

    public void DoSomething1()
    {
        // ...
        _dict.GetOrAdd(1, new MyWorker(Guid.NewGuid()));
        // ...
    }

    public void DoSomething2()
    {
        // ...
        _dict.TryRemove(1, out MyWorker worker);
        // ...
    }

    public void DoSomething3()
    {
        // OPTION 1: Won't it be possible for a different thread to remove
        // the object before the Reset() call?
        var myWorker = _dict.GetOrAdd(1, new MyWorker(Guid.NewGuid()));
        myWorker.Reset();
        // OPTION 2: Does method chaining somehow guarantee execution within
        // the same lock?
        _dict.GetOrAdd(1, new MyWorker(Guid.NewGuid())).Reset();
        // OPTION 3: ?
    }
}

public class MyWorker
{
    private int _someValue = 1;
    private IList<string> _someList = new List<string>();
    private Guid _immutableValue; // should not get reset

    public MyWorker(Guid immutableValue)
    {
        _immutableValue = immutableValue;
    }

    public void Reset()
    {
        _someValue = 1;
        _someList = new List<string>();
        // NB. do not change _immutableValue!
    }
}

在此示例中,不同的线程可能会以任何顺序多次调用 DoSomething1() 或 DoSomething2(),这可能导致字典中键为“1”的 MyWorker 随时被替换。

我需要 DoSomething3() 来调用 MyWorker.Reset() 并且知道如果该对象已经存在于字典中,那么将使用该对象。

  1. 选项 1 允许在调用 Reset 之前将检索到的 MyWorker 替换为新实例。
  2. 选项2好像也是一样的,除非在使用流畅的时候有特殊处理?

还有哪些其他选项可以检索值并在释放锁之前检索值的同一个锁内对其执行 method/operation?或者说,如何保证原子操作?我可以通过某种方式传递 lambda 吗?

ConcurrentDictionary is thread-safe in the sense that it protects its internal state from corruption. It does not protect from corruption the state of the objects it contains as keys or values. After retrieving an object from the ConcurrentDictionary using the GetOrAdd方法,如果你想改变这个对象的状态,而这个对象不是线程安全的,你负责通过使用锁或其他方式来同步多个线程对此对象的访问方法。换句话说,ConcurrentDictionary class 提供的线程安全保证仅限于自身。

您可能仍想使用这个 class,即使它不能满足您所有的线程安全需求,因为它的精细内部锁定实现在线程争用严重的情况下会非常有效。