C# 遍歷方法全對比:`Parallel.ForEach`、`List.ForEach`、`foreach` 到底怎么選?
遍歷集合是 C# 程序員天天要干的事。數據多了、邏輯復雜了,性能、異步、并發就統統成了問題。C# 提供了幾種不同的遍歷方式,各有優缺點,今天我們來用真實代碼和具體場景,一次講清楚:
- Parallel.ForEach 和 Parallel.ForEachAsync
- List<T>.ForEach
- foreach(包括配合異步方法)
1. Parallel.ForEach:多線程并發執行,性能猛獸
當你有大量數據需要同時處理,而且每個處理之間沒有依賴關系,用 Parallel.ForEach 能顯著提升性能。
Parallel.ForEach(myList, item =>
{
ProcessHeavy(item); // 耗時的同步任務
});
這個方法會自動幫你分配線程池中的線程去并發執行任務。唯一要注意的是,它不保證順序,多個任務是同時跑的。如果你訪問了共享資源(比如同一個文件、變量),就要手動加鎖或用線程安全的方式處理。
.NET 6 起還支持異步版本:
await Parallel.ForEachAsync(myList, async (item, token) =>
{
await ProcessAsync(item); // 異步耗時任務,如 HTTP 請求
});
這個非常適合需要同時跑多個異步請求,比如發起 100 個 API 調用、同時上傳一堆文件等。
適合場景:
- 并發執行沒有順序依賴的任務
- 大批量數據處理
- 高性能需求場景,如后臺服務、圖像處理等
2. List<T>.ForEach:優雅簡潔,但局限也多
很多人說的 “Enumerable.ForEach” 其實并不存在,真正的是 List<T>.ForEach 方法。它是 List 自帶的實例方法,不是 LINQ 擴展。
var list = new List<int> { 1, 2, 3 };
list.ForEach(item => Console.WriteLine(item));
看起來非常簡潔,適合快速寫小腳本或者 UI 層的簡單邏輯處理。但它只支持 List 類型,而且不能用于異步操作。你要是這樣寫:
list.ForEach(async item => await DoSomethingAsync(item)); // 錯誤寫法!
這段代碼會變成 async void,出了錯都捕不到,調試困難,不建議這樣使用。
適合場景:
- 小數據量操作
- 不涉及異步或并發的邏輯
- 代碼潔癖患者追求簡短寫法
3. foreach + async:穩妥靠譜,順序清晰
最經典的寫法仍然是 foreach,它的好處是穩。你可以明確知道順序、執行時機、異常處理,配合異步也很好用。
foreach (var item in myList)
{
await DoSomethingAsync(item); // 一個個執行
}
雖然不能并發,但非常適合對順序敏感的場景,比如依次寫數據庫、依次上傳文件、依次記錄日志等。
.NET 還支持 await foreach 遍歷異步流,比如從數據庫流式讀取數據:
await foreach (var row in GetDataAsync())
{
Console.WriteLine(row);
}
這類寫法適合消息隊列、數據庫分頁加載、SignalR 等場景。
適合場景:
- 需要確保執行順序
- 異步操作逐個進行,穩定性優先
- 可配合異步流讀取大數據量
4. 對比總結表
遍歷方式 | 是否支持并發 | 是否支持異步 | 順序是否保證 | 支持的集合類型 | 推薦使用場景 |
| ? | ? | ? | 所有 | 并行處理 CPU 密集型任務 |
| ? | ? | ? | 所有 | 并發處理異步任務(如接口、I/O) |
| ? | ?(?不支持) | ? | 僅限 | 小量數據處理,語法簡潔 |
| ? | ? | ? | 所有 | 順序異步執行,控制清晰 |
(異步流) | ? | ? | ? | 異步可枚舉對象 | 異步流處理,如數據庫流、消息流等 |
結語
- 如果你任務之間沒啥依賴,又想快, 并發用 Parallel.ForEach 或 Parallel.ForEachAsync 。
- 如果只是小腳本、小功能,List<T>.ForEach 最舒服,但別寫異步邏輯進去。
- 如果你想代碼靠譜、不出事,特別是對順序敏感的異步操作,還是老老實實用 foreach + await。