在.NET 6中,C#新增了Parallel.ForEachAsync方法,专门用于并行执行异步操作,解决了传统foreach循环串行执行异步任务效率不足的问题,也弥补了普通Parallel.ForEach无法处理异步委托的缺陷。该方法可以同时对多个元素执行异步操作,并且支持自定义并行度,适合处理批量IO密集型或网络请求类的异步任务。

Parallel.ForEachAsync基本语法
Parallel.ForEachAsync的核心作用是遍历一个集合,对每个元素执行异步委托,并且多个元素的异步操作可以并行执行。它的基本方法签名如下:
public static System.Threading.Tasks.Task ForEachAsync<TSource> (
System.Collections.Generic.IEnumerable<TSource> source,
System.Func<TSource,int,System.Threading.CancellationToken,System.Threading.Tasks.Task> body
)
其中source是要遍历的集合,body是处理每个元素的异步委托,委托参数包含当前元素、元素索引和取消令牌。如果需要控制并行度,还可以传入ParallelOptions参数:
public static System.Threading.Tasks.Task ForEachAsync<TSource> (
System.Collections.Generic.IEnumerable<TSource> source,
System.Threading.Tasks.ParallelOptions parallelOptions,
System.Func<TSource,int,System.Threading.CancellationToken,System.Threading.Tasks.Task> body
)
基础使用示例
下面通过一个简单的示例演示Parallel.ForEachAsync的基本用法,场景是批量请求接口获取不同用户的信息,使用并行异步循环提升请求效率:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// 待处理的用户ID集合
List<int> userIds = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Console.WriteLine($"开始执行并行异步循环,时间:{DateTime.Now:HH:mm:ss.fff}");
// 使用Parallel.ForEachAsync并行处理每个用户ID
await Parallel.ForEachAsync(userIds, async (userId, cancellationToken) =>
{
// 模拟异步请求接口获取用户信息,耗时1秒
await Task.Delay(1000, cancellationToken);
Console.WriteLine($"处理用户ID:{userId},完成时间:{DateTime.Now:HH:mm:ss.fff}");
});
Console.WriteLine($"所有任务完成,时间:{DateTime.Now:HH:mm:ss.fff}");
}
}
执行上述代码后会发现,10个用户ID的处理完成时间大约在1秒左右,因为默认并行度会根据系统CPU核心数自动调整,多个异步任务同时执行。如果使用普通foreach循环串行执行,完成时间会接近10秒。
控制并行度
默认情况下Parallel.ForEachAsync的并行度由运行时自动决定,如果需要手动限制同时执行的异步任务数量,可以通过ParallelOptions的MaxDegreeOfParallelism属性设置:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
List<int> userIds = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 创建并行配置,设置最大并行度为3
ParallelOptions options = new ParallelOptions
{
MaxDegreeOfParallelism = 3
};
Console.WriteLine($"开始执行,最大并行度3,时间:{DateTime.Now:HH:mm:ss.fff}");
await Parallel.ForEachAsync(userIds, options, async (userId, cancellationToken) =>
{
await Task.Delay(1000, cancellationToken);
Console.WriteLine($"处理用户ID:{userId},完成时间:{DateTime.Now:HH:mm:ss.fff}");
});
Console.WriteLine($"所有任务完成,时间:{DateTime.Now:HH:mm:ss.fff}");
}
}
设置最大并行度为3后,同时最多只有3个异步任务在执行,10个任务会分4批完成,总耗时大约4秒,适合需要限制资源占用的场景,比如避免同时发起过多网络请求导致服务端压力过大。
异常处理
Parallel.ForEachAsync执行过程中如果某个元素的异步操作抛出异常,所有正在执行的任务会被取消,剩余的未执行元素不会再处理,并且异常会被包装为AggregateException抛出。可以使用try-catch捕获异常并处理:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
try
{
await Parallel.ForEachAsync(numbers, async (num, cancellationToken) =>
{
if (num == 3)
{
// 模拟第三个元素处理时抛出异常
throw new InvalidOperationException($"处理数字{num}时发生错误");
}
await Task.Delay(500, cancellationToken);
Console.WriteLine($"成功处理数字:{num}");
});
}
catch (AggregateException ex)
{
// 遍历所有内部异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine($"捕获到异常:{innerEx.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"捕获到其他异常:{ex.Message}");
}
}
}
和其他循环方式的区别
很多开发者会混淆Parallel.ForEachAsync和以下几种循环方式,这里做简单对比:
| 循环方式 | 是否支持异步委托 | 是否并行执行 | 适用场景 |
|---|---|---|---|
| foreach + await | 是 | 否 | 需要串行执行异步任务的场景 |
| Parallel.ForEach | 否 | 是 | 处理CPU密集型的同步任务 |
| Parallel.ForEachAsync | 是 | 是 | 处理批量IO密集型或网络类的异步任务 |
注意事项
- Parallel.ForEachAsync只支持.NET 6及以上版本,低版本框架无法使用。
- 不要在body委托中执行长时间阻塞线程的同步操作,否则会失去并行的优势,甚至导致线程池耗尽。
- 如果需要保证元素的执行顺序和结果顺序一致,Parallel.ForEachAsync无法保证,需要额外处理顺序逻辑。
- 合理设置MaxDegreeOfParallelism,过高的并行度可能会导致系统资源过载,反而降低整体效率。
C#Parallel_ForEachAsync.NET_6异步循环修改时间:2026-07-22 15:30:32