You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将ConcurrentDictionary转为IDictionary时出现的异常并发行为

问题分析:ConcurrentDictionary转IDictionary后调用TryAdd抛出异常的原因

测试代码

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;               

public class Program
{
    public static void Main()
    {
        IDictionary<string, string> dictionary = new ConcurrentDictionary<string, string>(); // 有时会失败
        // ConcurrentDictionary<string, string> dictionary = new ConcurrentDictionary<string, string>(); // 始终正常运行

        Parallel.For(0, 10, i => dictionary.TryAdd("x1", "y1"));
    }
}

抛出的异常

未处理的异常。System.AggregateException: 发生一个或多个错误。(字典中已存在该键)

System.ArgumentException: 字典中已存在该键。

在 System.Collections.Concurrent.ConcurrentDictionary2.System.Collections.Generic.IDictionary<TKey,TValue>.Add(TKey key, TValue value) 在 System.Collections.Generic.CollectionExtensions.TryAdd[TKey,TValue](IDictionary2 dictionary, TKey key, TValue value)
在 Program.<>c__DisplayClass0_0.b__0(Int32 i)
在 System.Threading.Tasks.Parallel.<>c__DisplayClass19_02.b__1(RangeWorker& currentWorker, Int64 timeout, Boolean& replicationDelegateYieldedBeforeCompletion) --- 上一位置的堆栈跟踪结束 --- 在 System.Threading.Tasks.Parallel.<>c__DisplayClass19_02.b__1(RangeWorker& currentWorker, Int64 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
在 System.Threading.Tasks.TaskReplicator.Replica.Execute()
--- 内部异常堆栈跟踪结束 ---

疑问

但如果不将ConcurrentDictionary转换为IDictionary类型,代码似乎始终能正常运行。请问这是什么原因?


原因解析

核心差异在于调用的TryAdd方法完全不同:

1. 转成IDictionary时调用的是通用扩展方法

当变量类型为IDictionary<string, string>时,调用的TryAdd是CollectionExtensions类的通用扩展方法,它的实现逻辑大致为:

public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
    if (!dictionary.ContainsKey(key))
    {
        dictionary.Add(key, value);
        return true;
    }
    return false;
}

这个逻辑在多线程环境下存在竞态条件:线程A检查到键不存在,还没执行Add时,线程B也会检查到键不存在;线程A先完成Add后,线程B再执行Add就会触发IDictionary.Add的接口约定——键已存在时抛出ArgumentException。而ConcurrentDictionary实现IDictionary.Add时严格遵循了这个约定,因此会抛出异常。

2. 直接用ConcurrentDictionary时调用的是专属实例方法

当变量类型为ConcurrentDictionary<string, string>时,调用的是ConcurrentDictionary自身的专属实例方法TryAdd。这个方法是为并发场景设计的,内部通过原子操作(锁或CAS机制)保证「检查键是否存在」和「添加键值对」是一个不可分割的原子操作,不会出现竞态条件。即使多线程同时调用,只会有一个线程添加成功,其他线程返回false,不会抛出任何异常。


内容的提问来源于stack exchange,提问作者Daniel Fath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:33:35