使用Microsoft.CodeAnalysis.CSharp.Scripting时Parallel.For未等待完成的问题
问题分析与解决:Parallel.For操作非线程安全集合导致元素数量不足
你的代码中Parallel.For并非未等待完成就返回,问题核心是**List<T>不是线程安全集合**,多线程并发调用Add方法会引发竞争条件,导致部分元素添加失败,最终返回的列表元素数量不足且结果不稳定。这既不是Microsoft.CodeAnalysis.CSharp.Scripting的Bug,也不是该场景不受支持。
问题根源
List<T>的Add方法内部会修改私有数组和计数变量,这些操作没有线程同步机制。当多个线程同时执行Add时,可能出现以下情况:
- 两个线程同时读取当前计数,基于同一个值插入元素,导致其中一个元素被覆盖
- 数组扩容过程中,线程间操作冲突导致部分元素未被正确写入
而Parallel.For本身会等待所有迭代任务完成后才继续执行后续代码,返回结果异常和Parallel.For的等待机制无关。
修复方案
方案1:使用线程安全集合ConcurrentBag<T>
将List<string>替换为System.Collections.Concurrent命名空间下的ConcurrentBag<string>,它专门为多线程场景设计,所有操作都是线程安全的:
var code = @" var myList = new ConcurrentBag<string>(); Parallel.For(0, 300, i => { myList.Add(i.ToString()); }); return myList.ToList();";
方案2:对List.Add操作加锁
通过显式加锁确保同一时间只有一个线程执行Add操作,避免竞争:
var code = @" var myList = new List<string>(); var lockObj = new object(); Parallel.For(0, 300, i => { lock(lockObj) { myList.Add(i.ToString()); } }); return myList;";
方案3:使用PLINQ简化实现
如果只是需要生成包含指定范围字符串的列表,用PLINQ可以更简洁地完成线程安全的集合生成:
var code = @" return Enumerable.Range(0, 300).AsParallel().Select(i => i.ToString()).ToList();";
内容的提问来源于stack exchange,提问作者opitzh
相关产品推荐
相关产品推荐

