Parallel.For循环类捕获变量错误导致finalQuery值重复问题排查
错误原因排查
核心问题
修改后的代码存在跨线程共享变量修改的线程安全问题,所有并行迭代共享同一个finalQuery变量导致值被覆盖。
具体差异分析
- 旧代码逻辑
finalQuery定义在Parallel.For的委托内部,每个并行迭代都会创建独立的finalQuery变量,不同迭代的修改完全隔离,不会互相干扰,所以运行正常。 - 新代码逻辑
你将finalQuery的定义移动到了外层for (var i = 0; i < query.SelectedDatabases.Count; i++)的作用域内,所有Parallel.For启动的并行任务共享这同一个finalQuery变量,且你在委托内部直接修改这个共享变量:
finalQuery = GetAdaptedBaseQueryWithResearchItemsInserted(finalQuery, researchItems, query.SQLVariants[copyOfi]);
重复值产生原理
Parallel.For的多个迭代会同时在不同线程运行:
- 线程1刚完成
finalQuery的赋值,还没执行PerformSingleRun,线程2就已经修改了同一个finalQuery的值 - 最终多个迭代拿到的
finalQuery都是最后一次被覆盖的值,就会出现多次返回相同值的现象
修复方案
两种方案二选一即可:
- 回到旧代码写法,将
finalQuery的定义挪到Parallel.For的委托内部,每个迭代单独生成自己的查询对象 - 在委托内部先拷贝外层
finalQuery到本地变量,仅修改本地变量,示例如下:
Parallel.For( 0, numberOfRunsNeeded, parallelOptionsWithMaxDegreeOfParallelism, j => { var copyOfj = j; // 拷贝到本地变量,避免修改共享值 var localFinalQuery = finalQuery; if (researchItemsPresent) { var researchItems = ViewModel.ResearchItems[queryNumber].GetRange(copyOfj * itemsAtOnce, Math.Min(itemsAtOnce, ViewModel.ResearchItems[queryNumber].Count - (copyOfj * itemsAtOnce))); localFinalQuery = GetAdaptedBaseQueryWithResearchItemsInserted(localFinalQuery, researchItems, query.SQLVariants[copyOfi]); } PerformSingleRun(query, copyOfi, dataSource, localFinalQuery, copyOfj, viewModel); } );
内容的提问来源于stack exchange,提问作者user4691433
相关产品推荐
相关产品推荐

