C#提供了多种并行编程的实现方式,其中Parallel类和Task.WhenAll是最常用的两种方案,分别适用于不同的业务场景,能够帮助开发者充分利用多核CPU的计算能力,提升程序的执行效率。

Parallel并行类基础介绍
Parallel是.NET Framework 4.0引入的并行编程类,位于System.Threading.Tasks命名空间下,主要用于简化并行循环的实现。它提供了Parallel.For、Parallel.ForEach和Parallel.Invoke三个核心方法,能够将原本串行的循环任务拆分为多个并行执行的子任务,自动分配CPU核心资源。
使用Parallel之前需要先引入对应的命名空间:
using System.Threading.Tasks;
Parallel并行循环的使用方法
Parallel.For并行for循环
Parallel.For是传统for循环的并行版本,适用于已知循环次数的场景,它的参数和for循环类似,包含起始索引、结束索引和执行每个迭代的委托。
下面是一个简单的Parallel.For使用示例,计算1到1000的数字平方和:
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
long sum = 0;
// 并行计算1到1000的平方和
Parallel.For(1, 1001, i =>
{
// 由于多个线程会同时操作sum,需要加锁保证线程安全
lock (new object())
{
sum += i * i;
}
});
Console.WriteLine($"1到1000的平方和为:{sum}");
}
}
需要注意的是,Parallel.For中的迭代是并行执行的,如果迭代内部操作共享变量,必须做好线程同步,否则会出现数据错误。上面的示例中使用了lock语句保证sum变量的累加操作线程安全,实际开发中也可以根据场景使用Interlocked类实现更轻量级的同步。
Parallel.ForEach并行foreach循环
Parallel.ForEach是传统foreach循环的并行版本,适用于遍历集合的场景,支持遍历数组、List等可枚举对象。
下面的示例演示使用Parallel.ForEach并行处理一个字符串集合,将每个字符串转为大写:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static void Main()
{
List<string> names = new List<string> { "apple", "banana", "cherry", "date", "elderberry" };
Parallel.ForEach(names, name =>
{
string upperName = name.ToUpper();
Console.WriteLine($"原始名称:{name},大写名称:{upperName}");
});
}
}
Task.WhenAll的使用方法
Task.WhenAll用于等待多个异步Task全部执行完成,它返回的是一个Task,当所有传入的Task都完成时,这个返回的Task才会完成。和Parallel不同,Task.WhenAll更适用于多个独立的异步操作并行的场景,尤其是涉及IO操作的异步任务。
下面的示例演示使用Task.WhenAll并行执行多个异步HTTP请求:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
List<string> urls = new List<string>
{
"http://ipipp.com/page1",
"http://ipipp.com/page2",
"http://ipipp.com/page3"
};
List<Task<string>> tasks = new List<Task<string>>();
using (HttpClient client = new HttpClient())
{
foreach (string url in urls)
{
// 将每个请求任务加入任务列表
tasks.Add(client.GetStringAsync(url));
}
// 等待所有请求任务完成
string[] results = await Task.WhenAll(tasks);
Console.WriteLine($"共获取到{results.Length}个页面的内容");
}
}
}
如果需要处理Task.WhenAll执行过程中出现的异常,可以使用try-catch捕获AggregateException,因为多个任务抛出的异常会被包装到AggregateException中。
Parallel和Task.WhenAll的差异对比
两者的适用场景有明显区别,具体差异如下表所示:
| 对比项 | Parallel | Task.WhenAll |
|---|---|---|
| 适用场景 | CPU密集型任务的并行执行 | 异步IO任务、多个独立异步操作的等待 |
| 执行方式 | 阻塞当前线程,直到所有并行任务完成 | 异步等待,不会阻塞当前线程 |
| 返回值处理 | 无直接返回值,需要手动处理共享变量 | 可以返回所有任务的执行结果 |
| 异常处理 | 异常会直接抛出,需要内部处理 | 异常会包装为AggregateException |
使用注意事项
- 并行编程并不是越多越好,任务拆分本身有开销,如果任务执行时间很短,并行反而会降低性能。
- 使用Parallel时如果迭代内有共享资源操作,一定要做好线程同步,避免数据竞争问题。
- Task.WhenAll等待的任务如果数量过多,建议控制并发数量,避免占用过多系统资源。
- 调试并行代码时可以使用Visual Studio的并行任务调试窗口,查看各个任务的执行状态。
ParallelTask_WhenAllC#并行编程并行循环修改时间:2026-06-25 12:33:35