首页
学习
活动
专区
圈层
工具
发布

C# 遍历方法全对比:`Parallel.ForEach`、`List.ForEach`、`foreach` 到底怎么选?

遍历集合是 C# 程序员天天要干的事。数据多了、逻辑复杂了,性能、异步、并发就统统成了问题。C# 提供了几种不同的遍历方式,各有优缺点,今天我们来用真实代码和具体场景,一次讲清楚:

  • Parallel.ForEachParallel.ForEachAsync
  • List<T>.ForEach
  • foreach(包括配合异步方法)

1. Parallel.ForEach:多线程并发执行,性能猛兽

当你有大量数据需要同时处理,而且每个处理之间没有依赖关系,用 Parallel.ForEach 能显著提升性能。

代码语言:javascript
代码运行次数:0
复制
Parallel.ForEach(myList, item =>
{
    ProcessHeavy(item); // 耗时的同步任务
});

这个方法会自动帮你分配线程池中的线程去并发执行任务。唯一要注意的是,它不保证顺序,多个任务是同时跑的。如果你访问了共享资源(比如同一个文件、变量),就要手动加锁或用线程安全的方式处理。

.NET 6 起还支持异步版本:

代码语言:javascript
代码运行次数:0
复制
await Parallel.ForEachAsync(myList, async (item, token) =>
{
    await ProcessAsync(item); // 异步耗时任务,如 HTTP 请求
});

这个非常适合需要同时跑多个异步请求,比如发起 100 个 API 调用、同时上传一堆文件等。

适合场景:

  • 并发执行没有顺序依赖的任务
  • 大批量数据处理
  • 高性能需求场景,如后台服务、图像处理等

2. List<T>.ForEach:优雅简洁,但局限也多

很多人说的 “Enumerable.ForEach” 其实并不存在,真正的是 List<T>.ForEach 方法。它是 List 自带的实例方法,不是 LINQ 扩展。

代码语言:javascript
代码运行次数:0
复制
var list = new List<int> { 1, 2, 3 };
list.ForEach(item => Console.WriteLine(item));

看起来非常简洁,适合快速写小脚本或者 UI 层的简单逻辑处理。但它只支持 List 类型,而且不能用于异步操作。你要是这样写:

代码语言:javascript
代码运行次数:0
复制
list.ForEach(async item => await DoSomethingAsync(item)); // 错误写法!

这段代码会变成 async void,出了错都捕不到,调试困难,不建议这样使用。

适合场景:

  • 小数据量操作
  • 不涉及异步或并发的逻辑
  • 代码洁癖患者追求简短写法

3. foreach + async:稳妥靠谱,顺序清晰

最经典的写法仍然是 foreach,它的好处是稳。你可以明确知道顺序、执行时机、异常处理,配合异步也很好用。

代码语言:javascript
代码运行次数:0
复制
foreach (var item in myList)
{
    await DoSomethingAsync(item); // 一个个执行
}

虽然不能并发,但非常适合对顺序敏感的场景,比如依次写数据库、依次上传文件、依次记录日志等。

.NET 还支持 await foreach 遍历异步流,比如从数据库流式读取数据:

代码语言:javascript
代码运行次数:0
复制
await foreach (var row in GetDataAsync())
{
    Console.WriteLine(row);
}

这类写法适合消息队列、数据库分页加载、SignalR 等场景。

适合场景:

  • 需要确保执行顺序
  • 异步操作逐个进行,稳定性优先
  • 可配合异步流读取大数据量

4. 对比总结表

遍历方式

是否支持并发

是否支持异步

顺序是否保证

支持的集合类型

推荐使用场景

Parallel.ForEach

所有 IEnumerable

并行处理 CPU 密集型任务

Parallel.ForEachAsync

所有 IEnumerable

并发处理异步任务(如接口、I/O)

List<T>.ForEach

❌(⚠不支持)

仅限 List<T>

小量数据处理,语法简洁

foreach + await

所有 IEnumerable

顺序异步执行,控制清晰

await foreach(异步流)

异步可枚举对象

异步流处理,如数据库流、消息流等

结语
  • 如果你任务之间没啥依赖,又想快, 并发用 Parallel.ForEachParallel.ForEachAsync
  • 如果只是小脚本、小功能,List<T>.ForEach 最舒服,但别写异步逻辑进去。
  • 如果你想代码靠谱、不出事,特别是对顺序敏感的异步操作,还是老老实实用 foreach + await

如果你觉得这篇文章对你有帮助,不妨点赞、收藏或转发!

举报
领券