ConcurrentDictionary.GetOrAdd嵌套集合场景下的线程安全疑问
ConcurrentDictionary.GetOrAdd 嵌套集合场景的线程安全行为解析
核心行为说明
当多线程同时针对同一个不存在的key调用GetOrAdd时:
- 所有竞争线程都会执行你传入的工厂方法(即
new ConcurrentBag<TaskCompletionSource<string>>()),也就是会临时创建多个ConcurrentBag实例。 - 但最终只有一个线程创建的实例会被成功存入
ConcurrentDictionary,其他线程创建的实例会被直接丢弃(成为垃圾回收对象)。 - 关键:所有线程调用
GetOrAdd后得到的返回值,都是那个被成功存入字典的ConcurrentBag实例——不管这个实例是不是当前线程自己创建的。
你的代码实际表现
对应你这段代码:
var completions = subscribers.GetOrAdd(key, _ => new ConcurrentBag<TaskCompletionSource<string>>()); completions.Add(completion);
- 完全符合你的期望:所有线程最终都会拿到同一个
ConcurrentBag实例,并向其中添加元素,不会出现元素“丢失”的情况。 - 唯一的额外开销是:竞争期间会多创建几个无用的
ConcurrentBag实例,但这些实例会被垃圾回收,不会影响业务逻辑的正确性。
为什么不会出问题
因为GetOrAdd的内部实现保证了,无论有多少个线程执行了工厂方法,最终返回给所有调用者的都是字典中实际存储的那个值。所以后续的Add操作都是针对同一个线程安全的ConcurrentBag,自然能保证所有元素都被正确添加到同一个集合里。
内容的提问来源于stack exchange,提问作者Web FR
相关产品推荐
相关产品推荐

