使用Parallel.For循环赋值后图表显示异常问题求助
Parallel.For操作List<List>导致图表加载异常的原因与修复
核心问题
.NET的List<T>并非线程安全集合,用Parallel.For多线程调用list[i].Add()时,多个线程会同时修改同一个子List的内部结构(比如计数变量、底层数组),引发竞争条件:
- 元素顺序被打乱,和单线程逻辑的顺序不一致
- 集合内部计数错误,后续取元素时容易出现索引越界
- 极端情况会造成集合结构损坏,直接抛出异常
另外你还遗漏了原普通for里的null判断+break逻辑:普通for遇到第一个null就停止添加,但Parallel.For默认会遍历所有j,哪怕前面已经出现null,这会让最终List的元素数量、内容和单线程场景完全不同,图表加载时自然会出问题。
修复方案
方案1:给子List加锁+保留终止逻辑
在Add操作时给当前子List加锁,确保同一时间只有一个线程修改它,同时用ParallelLoopState实现遇到null就终止循环的逻辑:
public void subReadFile_Max_V(List<List<double>> list) { for(int i = 0; i <= clsGlobe.fileNum - 1; i++) { int _count = 2; Parallel.For(_count, clsGlobe.Row - 2, (j, state) => { var cellValue = clsGlobe._sheet._ListWorksheets[i].Cells[j, clsGlobe.max_v_posiotn].value; if (cellValue == null) { state.Stop(); // 终止所有线程的循环,和原break逻辑一致 return; } lock (list[i]) // 给当前子List加锁,避免多线程竞争 { list[i].Add(cellValue); } }); } }
方案2:用线程安全且有序的集合(可选)
如果不想手动加锁,可以使用System.Collections.Concurrent下的ConcurrentQueue<T>(保证元素顺序),最后再转成List:
public void subReadFile_Max_V(List<List<double>> list) { for(int i = 0; i <= clsGlobe.fileNum - 1; i++) { int _count = 2; var queue = new ConcurrentQueue<double>(); Parallel.For(_count, clsGlobe.Row - 2, (j, state) => { var cellValue = clsGlobe._sheet._ListWorksheets[i].Cells[j, clsGlobe.max_v_posiotn].value; if (cellValue == null) { state.Stop(); return; } queue.Enqueue(cellValue); }); list[i] = queue.ToList(); // 转成List赋值 } }
这个方案无需手动加锁,且能保证元素顺序和遍历顺序一致,适合对性能要求稍高的场景。
额外提醒
修复后要确保list[i]的元素数量、顺序和普通for循环的结果完全一致,否则后续图表加载时clsGlobe.lmaxVoltage.lMaxVoltage[i-1][j]的索引对应关系会出错,依然会导致异常。
内容的提问来源于stack exchange,提问作者莊秉翰
相关产品推荐
相关产品推荐

