C#怎么使用Parallel.ForEach C#并行循环加速处理

来源:这里教程网 时间:2026-02-21 17:38:08 作者:

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(); // 不再启动新任务
    }
});

基本上就这些。用对了能快几倍,用错了反而更慢或出错——核心就三条:无共享写、合理控并发、异常要拆包。

相关推荐