Parallel.ForEach与foreach运行结果不同:我的代码为何线程不安全?
问题原因
你写的Parallel实现不是线程安全的核心原因是存在共享变量的竞态条件,具体问题如下:
- 你将
int index变量声明在Parallel.ForEach循环的外部,并行执行的每个匿名委托都会捕获这个全局的index变量,所有线程共用同一个变量的内存地址,读写操作没有同步限制。
举个实际的执行异常场景:
- 线程A计算出当前关键词的索引值为12,刚完成对
index的赋值 - 还没等线程A执行
indexes.Add(index)操作,线程B就把自己计算出的索引值102赋值给了同一个index变量 - 线程A后续执行Add操作时,存入集合的就是被覆盖后的102,而非自己计算的12,最终统计的最小值就会出错
- 你用到的
ConcurrentBag本身是线程安全的集合,写入操作没有问题,问题完全来自外部共享的index变量。而普通的foreach是单线程顺序执行,不存在同时改写index的情况,所以结果符合预期。
修复方案
将index变量的声明挪到Parallel.ForEach的委托内部,让每个并行任务使用自己独立的局部变量,即可解决线程安全问题,修复后代码如下:
ConcurrentBag<int> indexes = new(); switch (Case) { case 1: Parallel.ForEach(KeywordTypes.GetImplementedNamedObjects(), inos => { // 把index声明放到委托内部,每个线程有独立的变量副本 int index = content.IndexOf($"/begin {inos}"); index = index == -1 ? content.Length : index; indexes.Add(index); }); int minIndex = indexes.Min(); return minIndex; }
内容的提问来源于stack exchange,提问作者MichaelS
相关产品推荐
相关产品推荐

