You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Parallel.For在不同CPU核心线程下的利用率差异咨询

问题分析:核心是共享资源竞争+任务调度不匹配

你的情况我碰到过好几次,本质是并行任务里的共享资源竞争和任务粒度与CPU线程数不匹配导致的,咱们具体说:

1. 共享List的线程安全瓶颈

List<T>本身不是线程安全的,当你在Parallel.For里同时做Add/Remove操作时,.NET内部会触发隐式的线程同步(没抛出异常算运气好)。在4核8线程的i7上,线程数量少,竞争冲突概率低,大部分线程都在做计算;但到了16核32线程的Ryzen上,32个线程同时抢同一个List的锁,大部分时间都在等待锁释放,实际执行计算的时间占比只有35%左右,自然CPU利用率上不去。

2. 任务粒度太小,调度开销占比过高

总共才250次循环,分给32个线程的话,平均每个线程才7-8次迭代。Parallel.For的任务调度本身是有开销的,当每个任务的计算量远小于调度开销时,线程大部分时间都在等待调度,而不是执行实际逻辑,这也会拉低CPU利用率。

3. Parallel.For默认调度与Ryzen SMT的适配问题

Ryzen的32线程是SMT(同步多线程),每个物理核心对应两个逻辑线程。Parallel.For默认会用所有逻辑线程,但在有共享资源竞争的场景下,超线程的收益会大幅下降——因为两个逻辑线程共享同一个物理核心的资源,当一个线程在等锁时,另一个线程也没法充分利用核心资源,反而增加了竞争。


解决办法:从资源竞争、任务粒度、调度策略三方面优化

1. 替换线程不安全的List为线程安全集合

直接把List<T>换成ConcurrentBag<T>(适合无序场景)或者ConcurrentQueue<T>,这些集合是专门为并行场景设计的,内部的同步机制比手动加锁更高效:

// 替换原来的List为ConcurrentBag
var concurrentCollection = new ConcurrentBag<YourObjectType>();

Parallel.For(0, 250, i => {
    // 值比较逻辑
    if (condition) {
        var newObj = new YourObjectType();
        concurrentCollection.Add(newObj);
        // 如果需要移除操作,优先最后统一过滤,不要在循环里实时操作
    }
});

如果必须做移除操作,建议最后统一过滤,而不是在并行循环里实时执行——移除本身就是高竞争行为。

2. 优化任务粒度,减少调度开销

250次循环太少,你可以把迭代打包成更大的任务块,比如每10次迭代作为一个子任务,这样每个线程处理的任务量更大,调度开销占比就低了:

int batchSize = 10;
int totalBatches = (int)Math.Ceiling(250.0 / batchSize);

Parallel.For(0, totalBatches, batchIdx => {
    int start = batchIdx * batchSize;
    int end = Math.Min(start + batchSize, 250);
    // 处理从start到end的迭代
    for (int i = start; i < end; i++) {
        // 原来的循环逻辑
    }
});

3. 调整Parallel.For的并行度,适配物理核心

把MaxDegreeOfParallelism设置为物理核心数(16),而不是默认的逻辑核心数(32),避免超线程带来的额外竞争:

var options = new ParallelOptions {
    MaxDegreeOfParallelism = 16 // Ryzen 1950X的物理核心数
};

Parallel.For(0, 250, options, i => {
    // 循环逻辑
});

4. 彻底消除共享资源竞争:用本地集合+最后合并

这是效率最高的方案——让每个线程在本地创建一个List,处理完自己的迭代后,再把本地List合并到全局集合,完全避免并行过程中的资源竞争:

var localCollections = new ConcurrentBag<List<YourObjectType>>();

Parallel.For(0, 250, i => {
    var localList = new List<YourObjectType>();
    // 值比较、对象创建,都操作localList,无竞争
    if (condition) {
        localList.Add(new YourObjectType());
    }
    // 如果有移除,直接在localList里处理
    localList.RemoveAll(item => item.SomeProperty == someValue);
    // 把本地集合加入全局容器
    localCollections.Add(localList);
});

// 最后合并所有本地集合到一个全局List
var finalList = localCollections.SelectMany(list => list).ToList();

这个方案几乎没有线程同步开销,能充分利用Ryzen的多核心优势,CPU利用率会大幅提升。


内容的提问来源于stack exchange,提问作者Caustix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:47:03