ConcurrentBag初始为null时Parallel.ForEach添加元素异常问题
问题:ConcurrentBag初始为null时,Parallel.ForEach无法正确添加元素?
测试场景
场景1:初始为null的情况
ConcurrentBag<string> test = null; List<string> list= new() { "1", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3" }; Parallel.ForEach(list, x => { (test ??= new()).Add("result"); });
调试时test的元素数量每次不同(如19/23个)。
场景2:提前实例化的情况
ConcurrentBag<string> test = new(); List<string> list= new() { "1", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3" }; Parallel.ForEach(list, x => { (test ??= new()).Add("result"); });
test始终有23个元素。
原因分析
问题出在test ??= new()这个操作上——它不是原子操作。
当多个线程并行执行时,可能会有多个线程同时判断test为null,然后各自创建新的ConcurrentBag<string>实例。这些线程会把"result"添加到自己创建的那个实例里,但最终只有最后一个完成赋值操作的实例会覆盖test变量。其他线程创建的实例因为没有被引用,里面的元素也就丢失了,这就导致最终test里的元素数量每次都不一样(取决于有多少线程各自创建了实例)。
而场景2中,test一开始就被实例化为同一个ConcurrentBag对象,所有线程都操作这个唯一的线程安全容器,Add方法本身是线程安全的,所以23个元素能全部正确添加进去。
内容的提问来源于stack exchange,提问作者Trekki
相关产品推荐
相关产品推荐

