在多核处理器时代,编写能够充分利用硬件资源的并行代码变得日益重要。C# 提供了任务并行库(Task Parallel Library,TPL),这是一套用于并行编程的高级API,旨在简化并行任务的创建、执行和管理。本文将深入探讨 TPL 的核心概念、主要组件、使用场景以及最佳实践。
TPL 的核心概念
TPL 基于任务(Task)的概念,任务表示异步操作,可以独立运行,并且可以并行执行。TPL 抽象了线程的复杂性,允许开发者专注于任务的逻辑,而不用担心线程的创建和管理。
主要组件
- Task:表示异步操作的基本构建块。
- Parallel:提供了静态方法,用于并行执行循环和自定义并行操作。
- Task.Run:用于在后台线程上执行代码。
- Dataflow:提供了一组类型,用于构建复杂的数据流管道。
- Parallel LINQ (PLINQ):允许LINQ查询以并行方式执行。
创建和运行任务
使用 Task.Run
Task.Run
是启动后台任务的最简单方法之一,它返回一个 Task
对象,该对象在任务完成时可用。
var result = Task.Run(() => ComputeExpensiveOperation());
int computationResult = result.Result; // 阻塞直到任务完成
使用 Parallel 类
Parallel
类提供了执行并行循环的方法,如 Parallel.For
和 Parallel.ForEach
。
Parallel.ForEach(sourceCollection, (item) => {
// 处理每个元素
});
并行 LINQ (PLINQ)
PLINQ 允许你将 LINQ 查询转换为并行执行,通过在查询前添加 .AsParallel()
。
var results = sourceCollection.AsParallel().Where(item => item.IsEven).Select(item => item * 2).ToArray();
Dataflow
Dataflow 是 TPL 中的一个高级组件,它允许构建复杂的数据流管道。
代码语言:javascript复制var block = new TransformBlock<int, int>(x => x * x);
block.LinkTo(new BatchBlock<int>(10));
block.Post(1);
block.Post(2);
// ...
block.Complete();
错误处理
在 TPL 中,任务可能会引发异常。异常会被捕获并包装在 AggregateException
中。
try
{
var result = await Task.Run(() => {
if (someCondition) throw new Exception("Error occurred");
return 42;
});
}
catch (Exception ex)
{
// 处理异常
}
性能注意事项
并行编程可以显著提高性能,但也引入了额外的复杂性。开发者需要注意以下几点:
- 避免竞态条件:确保任务之间不会相互干扰。
- 不要过度并行化:过多的并行任务可能会导致上下文切换和资源争用,反而降低性能。
- 使用异步方法:对于I/O密集型操作,使用
async
和await
可以提高响应性和吞吐量。