c# 并行编程 Parallel.For 和普通 for 循环的区别

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

Parallel.For 会自动分配线程,但不保证执行顺序

普通

for
循环是严格串行、按索引递增顺序执行的;
Parallel.For
则把迭代范围切分成若干块,交由
ThreadPool
中的多个线程并行处理。这意味着:
i
的实际执行顺序完全不可预测,甚至可能
i=100
先于
i=1
完成。

常见错误现象:在循环体里直接修改共享变量(如

sum += array[i]
),没加锁或用原子操作,结果随机出错。

适用场景:各次迭代完全独立,无读写竞争,比如批量图像缩放、数组元素逐个计算哈希 不适用场景:需要按顺序累积状态(如前缀和)、依赖上一轮结果(如斐波那契递推)、或写入同一文件/数据库表且要求顺序落盘 若必须保序且想并行,改用
Parallel.ForEach
+
AsOrdered()
(性能代价大,慎用)

异常处理机制完全不同

普通

for
遇到异常立刻中断,堆栈清晰;
Parallel.For
中任一线程抛出异常,整个操作会封装为
AggregateException
抛出,内含所有子异常。

典型错误:只用

try-catch(Exception)
捕获,漏掉外层
AggregateException
,导致程序崩溃。

正确做法:用
try-catch(AggregateException ex)
,再遍历
ex.InnerExceptions
处理每个子异常
也可用
ParallelOptions.MaxDegreeOfParallelism
限制并发数,降低异常爆发密度
若某次迭代失败可忽略,应在循环体内自行
try-catch
,避免污染整体执行

取消操作需显式传入 CancellationToken

普通

for
要中断只能靠
break
或条件判断;
Parallel.For
支持协作式取消,但必须手动传入
CancellationToken
,否则调用
cancellationTokenSource.Cancel()
没反应。

var cts = new CancellationTokenSource();
Parallel.For(0, 1000, new ParallelOptions { CancellationToken = cts.Token }, i =>
{
    if (cts.Token.IsCancellationRequested) return;
    // 实际工作
});
cts.Cancel(); // 此时正在执行的迭代会继续,新分配的迭代不再启动
CancellationToken
只影响任务调度,不强制终止正在运行的线程(.NET 不支持强行 Abort)
循环体内需主动检查
Token.IsCancellationRequested
并及时退出,否则无法响应取消
未传
ParallelOptions
时,默认无取消支持,
Cancel()
完全无效

性能不是“开并行就一定快”,小数据量反而更慢

Parallel.For
启动有开销:任务拆分、线程调度、同步协调。对少量迭代(比如
for (i = 0; i )或单次耗时极短的操作(如简单加法),并行反而比普通 <code>for
慢 2–5 倍。

经验阈值:单次迭代平均耗时 > 1ms 且总迭代数 > 100 时,并行才大概率带来收益
Stopwatch
实测对比,别凭感觉——尤其注意首次运行受 JIT 影响,应预热后测
内存带宽瓶颈时(如大量数组拷贝),增加线程数可能因争抢缓存而降速

真正难的是识别「哪些迭代能并行」和「哪些共享状态必须保护」,而不是换一个关键字。写完

Parallel.For
后,务必验证结果正确性、异常行为、取消响应和真实负载下的性能表现。

相关推荐