ConcurrentDictionary AddOrUpdate方法抛出IndexOutOfRangeException

问题描述 投票:-3回答:1

作业正在通过不同的线程添加到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)
c# multithreading hashset concurrentdictionary
1个回答
1
投票

ConcurrentDictionary.AddOrUpdate方法的文档中:

为了对字典进行修改和写操作,ConcurrentDictionary.AddOrUpdate使用细粒度的锁定来确保线程安全。 (对字典的读取操作以无锁的方式执行。)但是,ConcurrentDictionary<TKey,TValue>addValueFactory委托在锁之外被调用可以避免由于在锁下执行未知代码而引起的问题。因此,对于updateValueFactory类上的所有其他操作,AddOrUpdate不是原子的。

(添加了重点)

因此,您不能将ConcurrentDictionary<TKey,TValue>用作HashSet的值,并且无法在没有保护的情况下从多个线程更新它。它将损坏,并开始引发随机异常,如您观察到的异常。您应该使用锁来保护它(每个ConcurrentDictionary使用不同的锁定对象以减少争用),或者使用HashSet(没有concurrent HashSet类,因此必须使用嵌套的HashSet)。 >

关于第一个选项,即涉及ConcurrentHashSet的选项,您应该在访问相同ConcurrentDictionary的所有位置使用相同的锁定对象,而不仅是在内部lock方法的回调。

尽管有可能通过使用工作流方法(HashSet库支持的方法)消除所有增加应用程序开销的同步。否则可能无法实现。这取决于您正在执行的操作。

© www.soinside.com 2019 - 2024. All rights reserved.