我如何对键可能尚未在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()并知道如果该对象已存在于字典中,那么将使用该对象。
在释放锁之前,还有哪些其他选项可用于检索值并在与检索该值相同的锁内对其执行方法/操作?换句话说,如何保证原子操作?我可以通过lambda吗?
答案 0 :(得分:1)
ConcurrentDictionary
在保护其内部状态不受损坏的意义上是线程安全的。它不能保护其包含为键或值的对象的状态免于损坏。使用GetOrAdd
方法从ConcurrentDictionary
检索对象后,如果您想改变该对象的状态并且该对象不是线程安全的,则您负责同步从该对象对该对象的访问。使用锁或其他方式的多个线程。换句话说,ConcurrentDictionary
类提供的线程安全保证仅限于自身。
即使它不能满足您所有的线程安全需求,您仍可能要使用此类,因为在高线程争用的情况下,其精细的内部锁定实现可能非常有效。