【问题标题】:ConcurrentDictionary AddOrUpdate method throwing IndexOutOfRangeExceptionConcurrentDictionary AddOrUpdate 方法抛出 IndexOutOfRangeException
【发布时间】:2019-11-08 21:09:50
【问题描述】:

作业被不同的线程添加到HashSet 并引发此错误。有什么解决办法吗?

ConcurrentDictionary<myKey, HashSet<Job>> _dictKeyJob;

_dictKeyJob.AddOrUpdate(myKey, key =>
{
    return new HashSet<Job>({ Job };
}, (key, hashJobs) =>
{
    if (Job.Status == eStatus.Cancelled)
    {
        hashJobs.Remove(Job);
    }
    else
    {
        hashJobs.Add(Job);
    }
    return hashJobs;
});

例外:

System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Collections.Generic.HashSet`1.SetCapacity(Int32 newSize, Boolean forceNewHashCodes)
   at System.Collections.Generic.HashSet`1.AddIfNotPresent(T value)
   at Raj.OPS.Common.Test.<>c__DisplayClass38_0.<SetOrAddKey>b__1(mKey key, HashSet`1 hashJobs) in 
   at System.Collections.Concurrent.ConcurrentDictionary`2.**AddOrUpdate**(TKey key, Func`2 addValueFactory, Func`3 updateValueFactory)

【问题讨论】:

  • 你能显示添加到字典方法的代码吗?
  • 如果您能提供minimal reproducible example,那就太好了。我的水晶球建议您将HashSet 存储为ConcurrentDictionary 中的值。 ConcurrentDictionary 不会神奇地使 HashSet 线程安全。
  • stackoverflow.com/questions/4307131/… 可能是合适的副本。
  • 刚刚添加了确切的代码。

标签: c# multithreading hashset concurrentdictionary


【解决方案1】:

来自ConcurrentDictionary.AddOrUpdate方法的文档:

对于字典的修改和写入操作,ConcurrentDictionary&lt;TKey,TValue&gt; 使用细粒度锁定来确保线程安全。 (对字典的读取操作以无锁方式执行。)但是,addValueFactoryupdateValueFactory 委托在锁外调用以避免执行未知代码可能出现的问题在锁下。因此,AddOrUpdate 对于ConcurrentDictionary&lt;TKey,TValue&gt; 类上的所有其他操作而言不是原子的。

(强调)

所以你不能使用HashSet作为ConcurrentDictionary的值,并在没有保护的情况下从多个线程更新它。它会损坏,并开始抛出随机异常,就像你观察到的那样。您应该使用锁保护它(为每个HashSet 使用不同的锁定对象以减少争用),或者使用concurrent HashSet(没有ConcurrentHashSet 类,因此您必须使用嵌套的ConcurrentDictionary)。

关于第一个选项,即涉及lock 的选项,您应该在访问相同HashSet 的任何地方使用相同的锁定对象,而不仅仅是在 AddOrUpdate 方法的回调。

尽管使用工作流方法(TPL Dataflow 库支持的方法)可以消除所有这些增加应用程序开销的同步。或者这可能是不可能的。这取决于你在做什么的细节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多