并发字典正确用法

Posted

技术标签:

【中文标题】并发字典正确用法【英文标题】:Concurrent Dictionary Correct Usage 【发布时间】:2012-01-03 18:37:40 【问题描述】:

我认为这是对并发字典的正确使用是否正确

private ConcurrentDictionary<int,long> myDic = new ConcurrentDictionary<int,long>();

//Main thread at program startup

for(int i = 0; i < 4; i++)

  myDic.Add(i, 0);


//Separate threads use this to update a value

myDic[InputID] = newLongValue;

我没有锁等,我只是在更新字典中的值,即使多个线程可能正在尝试做同样的事情。

【问题讨论】:

这取决于 - newLongValue 是否取决于 myDic[InputID] 的先前值? 您应该避免直接通过密钥访问myDic[InputID] 以获取竞态条件。你应该试试GetOrAdd @OlivierAlbertini,我认为myDic[InputID] 在用作左值时不会引起任何问题。 GetOrAdd 不是正确的替换,因为它仅在值不存在时才添加。我们可以改为使用AddOrUpdate 在字典中添加/更新相同的值。 @JatinSanghvi 这里的旧响应,但我想说是的,直接使用访问器会导致问题,因为它绕过了原子操作。根据文档:“无条件地将键/值对存储在字典中,并覆盖已存在的键的值”。关键词是无条件的,如果你在更新时避免并发/原子功能,那么使用并发字典是没有意义的。 【参考方案1】:

这取决于你所说的线程安全。

来自 MSDN - How to: Add and Remove Items from a ConcurrentDictionary:

ConcurrentDictionary&lt;TKey, TValue&gt; 专为多线程场景而设计。您不必在代码中使用锁来添加或删除集合中的项目。但是,一个线程总是可以检索一个值,而另一个线程通过给同一个键一个新值来立即更新集合。

因此,可能会在字典中获得不一致的视图

【讨论】:

这是一个有趣的观点!在那种情况下你还会使用锁吗? @Jon - 这取决于您的应用程序以及您是否可以。但我想说,如果您想要项目的一致视图,则需要将项目的每次读取和更新包装在锁中。 我认为这不是文档所说的。不一致与视图包含的内容有关,如果视图只是值,那么它是完全一致的。只要你得到一个键的值,字典中键的值就可以改变。这与 DateTime.Now 值一样不一致。【参考方案2】:

找出这一点的最佳方法是查看 MSDN 文档。

对于 ConcurrentDictionary,页面为 http://msdn.microsoft.com/en-us/library/dd287191.aspx

在线程安全部分,声明“ConcurrentDictionary(Of TKey, TValue) 的所有公共和受保护成员都是线程安全的,并且可以从多个线程同时使用。”

所以从并发的角度来看你没问题。

【讨论】:

【参考方案3】:

是的,你是对的。

这以及在一个线程上枚举字典同时在另一个线程上更改字典的可能性是该类存在的唯一方式。

【讨论】:

我要补充的是here 是关于如何以及何时使用ConcurrentDictionary 的有用信息。【参考方案4】:

这取决于,在我的情况下,我更喜欢使用这种方法。

ConcurrentDictionary<TKey, TValue>.AddOrUpdate Method (TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>);

有关方法使用详情,请参阅MSDN Library。

示例用法:

results.AddOrUpdate(
  Id,
  id => new DbResult() 
     Id = id,
     Value = row.Value,
     Rank = 1
  ,
  (id, v) =>
  
     v.Rank++;
     return v;
  );

【讨论】:

仅供参考:“当您提供值工厂方法(给 GetOrAdd 和 AddOrUpdate 方法)时,它实际上可以运行并在之后丢弃其结果(因为其他线程赢得了比赛)。”更多信息在这里:arbel.net/2013/02/03/… 是的,你是对的,正如备注部分所述“如果你在不同的线程上同时调用 AddOrUpdate,addValueFactory 可能会被多次调用,但它的键/值对可能不会被添加到每个电话的字典。”所以你需要确保你没有生成多个持久对象。 而如果你需要更新内容,而不是完全改变存储的对象,例如改变之前添加的对象的属性,这个方法很有用,否则你需要使用锁或其他同步方法.【参考方案5】:

请注意:不能证明使用具有线性循环的 ConcurrentDicitonary 对象是合理的,从而使其未得到充分利用。最好的替代方法是遵循 Microsoft 文档的建议,正如 Oded 所提到的,使用 Parallelism,根据以下示例:

Parallel.For(0, 4, i => 

   myDic.TryAdd(i, 0);
);

【讨论】:

以上是关于并发字典正确用法的主要内容,如果未能解决你的问题,请参考以下文章

并发编程 - LockSupport 应用

字典对象的 Pythonic 用法(上篇)

浅谈python 字典对象的 Pythonic 用法

字典的用法

python 字典的用法

dataframe中stu用法