如何判断ConcurrentDictionary.GetOrAdd操作是找到值还是新增值?
如何判断ConcurrentDictionary.GetOrAdd是获取已有值还是新增值
核心思路
ConcurrentDictionary.GetOrAdd本身不会直接返回是否新增的状态,但可以通过两种简单方式实现判断:
方法一:利用工厂函数的执行特性(通用型)
GetOrAdd的工厂函数仅在键不存在时才会执行,所以可以在工厂函数内部标记“新增”状态,代码如下:
var concurrentDict = new ConcurrentDictionary<string, string>(); string targetKey = "user_001"; string newValue = "Sam"; bool isNewlyAdded = false; var finalValue = concurrentDict.GetOrAdd(targetKey, key => { // 只有当键不存在时,这段代码才会运行 isNewlyAdded = true; return newValue; }); // 结果判断 if (isNewlyAdded) { Console.WriteLine($"键 {targetKey} 不存在,已新增值:{finalValue}"); } else { Console.WriteLine($"键 {targetKey} 已存在,获取到已有值:{finalValue}"); }
这个方法对值类型、引用类型都适用,完全符合并发安全要求——工厂函数只会被执行一次,不会出现多线程重复标记的问题。
方法二:引用类型实例对比(仅适用于引用类型)
如果存储的是引用类型,可以对比GetOrAdd的返回值和你传入/创建的实例:
public class UserInfo { public string Name { get; set; } } var concurrentDict = new ConcurrentDictionary<string, UserInfo>(); string targetKey = "user_001"; var newUser = new UserInfo { Name = "Sam" }; var resultUser = concurrentDict.GetOrAdd(targetKey, newUser); // 引用相等说明是本次新增的实例,否则是已存在的旧实例 bool isNewlyAdded = ReferenceEquals(resultUser, newUser);
注意:这个方法只适用于引用类型,值类型因为是值传递,ReferenceEquals永远返回false,不能用这种方式。
避坑提醒
- 不要先调用
TryGetValue再调用Add,高并发场景下会有线程安全问题——两个线程可能同时通过TryGetValue判断键不存在,然后都尝试Add导致异常。 - 工厂函数内部不要做耗时操作,否则会阻塞其他等待该键的线程。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

