都说ConcurrentDictionary<TKey, TValue>有陷阱

本文解析了ConcurrentDictionary在多线程环境下如何确保数据的一致性和并发处理能力。通过实例演示了当多个线程同时尝试添加同一键值对时,ConcurrentDictionary如何避免重复添加,并讨论了其内部委托执行机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看这么几句解释(英文原帖):

private static void ConcurrentDictionary()
{
    var dict = new ConcurrentDictionary<int, string>();
    ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1));
    ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1));
}
  
private static WaitCallback LongGetOrAdd(ConcurrentDictionary<int, string> dict, int index)
{
    return o => dict.GetOrAdd(index,i =>
                                        {
                                            Console.WriteLine("Adding!");
                                            Thread.SpinWait(1000);
  
                                            return i.ToString();
                                        }
                    );
}
View Code

 

1) threadA calls GetOrAdd, finds no item and creates a new item to Add by invoking the valueFactory delegate.

线程A调用GetOrAdd,发现数据不存在并创建了一条新数据,准备调用委托方法添加数据

2) threadB calls GetOrAdd concurrently, its valueFactory delegate is invoked and it arrives at the internal lock before threadA, and so its new key-value pair is added to the dictionary.

线程B现在也在调用GetOrAdd方法,它的委托被调用了,比线程A先进入GetOrAdd内部的锁,因此它创建的键值对被添加到dict中了

3) threadA’s user delegate completes, and the thread arrives at the lock, but now sees that the item exists already

线A的委托执行完成了,并且也获得了GetOrAdd的锁,但是现在发现相关数据已经存在了

4) threadA performs a "Get", and returns the data th at was previously added by threadB.

线程A执行“Get”,返回之前被线程B添加的数据

 

不止这个英文原帖,好多国内的帖子都在说这个问题。

然而我觉得这并不是个问题,这最多是一个编程时需要注意的事项,微软的这个设计是合理的:ConcurrentDictionary没有理由也没有职责去把外部代码加锁,外部代码的同步应该由

外部控制。回头看看上面的解释,我们发现只不过是传递给ConcurrentDictionary的委托被执行了不止一次,但数据仍然只添加了一次,ConcurrentDictionary已经完全履行了自己的职责,而且MSDN也作了相关的说明(事实上,上面的四句英文也来自MSDN):

The user delegate that is passed to these methods is invoked outside of the dictionary's internal lock. (This is done to prevent unknown code from blocking all threads.) 

传递进来的用户委托在字典内部锁的外面调用,这么做是为了防止阻塞所有相关线程,具体参考MSDN

转载于:https://www.cnblogs.com/qzhforthelife/p/5958953.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值