如何在C#中获取List<T>.AsParallel()的执行结果?
问题分析
你这段代码的核心问题有两个:
AsParallel().ForAll不支持异步委托——你传入的asynclambda会被当作async void执行,属于“fire-and-forget”(只管触发不管结果)的异步操作,不仅没法收集返回值,程序还可能在任务没跑完就继续往下执行。- PLINQ(即
AsParallel相关API)是针对CPU密集型任务设计的,而你的Test方法是IO密集型(用Task.Delay模拟IO等待),用它反而会浪费资源。
最优解决方案:IO密集型场景用Task.WhenAll
对于IO密集型的异步任务,直接用Task.WhenAll配合LINQ生成任务列表是最高效的,完全不需要PLINQ:
var x = new[] { 0, 1 }; var y = new[] { 2, 3 }; // 先生成所有要执行的异步任务 var tasks = x.Zip(y, (xVal, yVal) => Test(xVal, yVal)).ToList(); // 等待所有任务完成,一次性获取所有结果 var results = await Task.WhenAll(tasks); // 此时results就是你要的[0,1]
这个方案的优势:
- 天然适配异步逻辑,能正确等待所有任务结束,完美收集返回值。
- IO密集型任务不需要占用大量线程,线程池会自动调度,资源利用率比PLINQ高得多。
CPU密集型场景的PLINQ正确用法
如果后续你的Test改成CPU密集型操作(比如复杂计算),再用PLINQ才合理,而且要改用Select来收集结果,而非ForAll:
// 假设Test变成同步CPU密集方法:static int Test(int x, int y) { /* 耗时计算逻辑 */ } var zipped = x.Zip(y, (xVal, yVal) => new { xVal, yVal }).ToList(); var results = zipped.AsParallel() .Select(pair => Test(pair.xVal, pair.yVal)) .ToList(); // 如果还是异步但CPU密集(比如内部用Task.Run封装计算),可以这样写: var results = zipped.AsParallel() .Select(pair => Test(pair.xVal, pair.yVal).Result) .ToList();
你还可以用WithDegreeOfParallelism手动指定并行度,比如.AsParallel().WithDegreeOfParallelism(4),控制同时运行的任务数量。
原代码失效的本质原因
ForAll的参数是Action<T>,当你传入async lambda时,它会被解析成async void方法——这种方法在异步编程里专门用于事件处理,它不会返回Task,所以你既没法等待它完成,也拿不到返回值,甚至任务抛出异常都没法主动捕获(除非加全局异常处理),完全不适合需要收集结果的场景。
内容的提问来源于stack exchange,提问作者Nicolas REY
相关产品推荐
相关产品推荐

