在C#中,Task.Run配合Task.WhenAll和Parallel类都是用于并行处理以提高性能的常见方法,但它们的使用场景和内部机制有所不同。
1. Task.Run + Task.WhenAll
这种方法适用于I/O密集型操作(如网络请求、文件读写等)或CPU密集型操作,但更侧重于异步操作。它通过将多个任务并行启动,然后等待所有任务完成。
使用场景:
- 当操作是异步的(如使用
async/await)时,这种方法更自然。 - 每个任务相对独立,不需要共享资源(如果共享资源,需要注意线程安全)。
- 任务数量不是特别大(虽然可以创建很多任务,但要注意资源限制)。
2. Parallel类
Parallel类(Parallel.For,Parallel.ForEach)主要用于数据并行,适用于CPU密集型操作,它使用多个线程来并行处理数据集合。它是基于任务的,但内部使用线程池,并且是同步的(阻塞当前线程直到所有操作完成)。
使用场景:
- CPU密集型操作,例如大量计算。
- 处理集合中的每个元素,且每个元素的处理是独立的(或通过线程安全的方式共享状态)。
- 当操作是同步的,并且你希望利用多核处理器。
在C#中,Task.Run + Task.WhenAll 和 Parallel 类都是并行处理的技术,但适用场景和实现方式不同。以下是选择建议和对应的取消操作实现:
一、如何选择?
| 特性 | Task.Run + Task.WhenAll | Parallel |
|---|---|---|
| 适用场景 | I/O密集型操作、异步任务、独立任务 | CPU密集型操作、数据并行处理 |
| 并行控制 | 手动控制(通过任务列表) | 自动分区(通过ParallelOptions配置) |
| 阻塞性 | 非阻塞(async/await) | 阻塞(同步操作) |
| 任务粒度 | 粗粒度(独立任务) | 细粒度(数据集合元素) |
| 异常处理 | 通过AggregateException捕获所有异常 |
同左 |
| 资源开销 | 较高(每个任务独立调度) | 较低(优化线程池使用) |
选择建议:
-
优先用
Task.WhenAll:- 处理 I/O密集型 操作(如API调用、文件读写)
- 需要 异步等待 结果时(避免阻塞线程)
- 任务逻辑 独立且数量动态变化 时
-
优先用
Parallel:- 处理 CPU密集型 计算(如图像处理、数值计算)
- 需要 高效处理数据集合(如数组、列表的并行循环)
- 需要 限制并发度(通过
MaxDegreeOfParallelism)
二、取消操作实现
1. Task.Run + Task.WhenAll 的取消
使用 CancellationTokenSource 传递取消令牌。
public async Task RunTasksWithCancellationAsync()
{var cts = new CancellationTokenSource();// 创建任务列表(传入取消令牌)var tasks = new List<Task>();for (int i = 0; i < 10; i++){tasks.Add(Task.Run(() => DoWorkAsync(cts.Token), cts.Token));}// 外部触发取消(例如超时或用户操作)cts.CancelAfter(TimeSpan.FromSeconds(5)); // 5秒后自动取消try{await Task.WhenAll(tasks);}catch (OperationCanceledException){Console.WriteLine("任务已取消");}
}private async Task DoWorkAsync(CancellationToken token)
{while (!token.IsCancellationRequested){await Task.Delay(1000, token); // 模拟异步操作token.ThrowIfCancellationRequested(); // 检查取消}
}
2. Parallel 的取消
通过 ParallelOptions 传递取消令牌。
public void RunParallelWithCancellation()
{var cts = new CancellationTokenSource();var options = new ParallelOptions{CancellationToken = cts.Token,MaxDegreeOfParallelism = 4 // 限制并发数};cts.CancelAfter(TimeSpan.FromSeconds(5)); // 5秒后取消try{Parallel.For(0, 100, options, (i, state) =>{options.CancellationToken.ThrowIfCancellationRequested();DoCpuBoundWork(i); // 模拟CPU密集型操作});}catch (OperationCanceledException){Console.WriteLine("并行循环已取消");}
}private void DoCpuBoundWork(int index)
{Thread.Sleep(500); // 模拟CPU工作
}
三、关键注意事项
- 资源释放:
- 在取消后及时释放资源(如数据库连接、文件句柄)。
- 取消响应:
- 在循环或长时间操作中 定期检查
token.IsCancellationRequested。
- 在循环或长时间操作中 定期检查
- 异常聚合:
- 两者都会抛出
AggregateException,需遍历InnerExceptions处理具体错误。
- 两者都会抛出
- 异步兼容性:
Parallel不支持async委托(内部用同步方法),若需异步并行循环,使用Task.WhenAll。
四、总结
Task.WhenAll:适合 异步、I/O密集型、异构任务。Parallel:适合 同步、CPU密集型、同构数据并行。- 取消机制:统一通过
CancellationToken实现,注意在任务内部定期检查取消状态。
