Parallel.ForEach可自动并行化CPU密集型任务,但需确保线程安全、避免共享状态、慎用于I/O场景;基本用法是替换foreach并使用System.Threading.Tasks;推荐用局部状态+合并方式处理聚合结果,配合ParallelOptions控制并发度,异常需捕获AggregateException并遍历InnerExceptions。

用 Parallel.ForEach 能让集合遍历自动并行化,显著提升 CPU 密集型任务的执行速度,但不是所有场景都适合——关键看是否线程安全、有无共享状态、是否 I/O 主导。
基本用法:替换普通 foreach
把原来的串行循环改成并行,只需两步:
- 引用
System.Threading.Tasks - 用
Parallel.ForEach(集合, item => { /* 处理逻辑 */ })替代foreach
例如处理一个整数列表求平方:
(注意:下面代码不涉及共享变量,是安全的)var numbers = Enumerable.Range(1, 100000).ToList();
Parallel.ForEach(numbers, n => {
var result = n * n; // 纯计算,无副作用
// 可以写入线程本地变量或加锁写入共享结构
});
避免共享资源冲突:别直接改公共变量
多个线程同时写同一个变量(如 sum += n)会导致结果错误。正确做法有:
- 用
Interlocked.Add(ref sum, n)原子操作(适合简单数值累加) - 用
lock(obj)包裹临界区(开销略大,但通用) - 用
Parallel.ForEach的重载 + 局部状态(推荐,性能最好)
局部状态示例(每个线程独立累加,最后合并):
(适用于需要汇总结果的场景)int total = Parallel.ForEach(
numbers,
() => 0, // 每个线程的局部初始值
(n, loopState, localSum) => localSum + n * n, // 局部处理
localSum => Interlocked.Add(ref total, localSum) // 合并到全局
);
控制并发度:防止线程过多拖慢性能
默认会根据 CPU 核心数动态调整线程数,但遇到大量小任务或内存敏感场景,可手动限制:
- 传入
new ParallelOptions { MaxDegreeOfParallelism = 4 } - 一般设为
Environment.ProcessorCount或略高,避免上下文切换开销 - 特别耗内存的任务(如图像处理),适当调低更稳
提前退出和异常处理
不能用 break 或 return 直接跳出整个循环,但可以:
- 调用
loopState.Break():通知后续分区尽快停止(已开始的仍会完成) - 调用
loopState.Stop():尽量立即中止所有未开始的迭代 - 异常会被包装成
AggregateException,需try-catch并遍历.InnerExceptions
例如查找第一个满足条件的元素:
int? found = null;
Parallel.ForEach(numbers, (n, loopState) => {
if (n > 50000) {
found = n;
loopState.Stop(); // 不再启动新任务
}
});
基本上就这些。用对了能快几倍,用错了反而更慢或出错——核心就三条:无共享写、合理控并发、异常要拆包。









