C# Task 基础
介绍
在 C# 中,Task
是异步编程的核心概念之一。它代表一个异步操作,允许你在不阻塞主线程的情况下执行耗时操作。通过使用 Task
,你可以编写高效、响应迅速的应用程序,尤其是在处理 I/O 操作、网络请求或计算密集型任务时。
Task
是 System.Threading.Tasks
命名空间的一部分,它提供了一种简单的方式来管理异步操作的状态、结果和异常。
Task 的基本概念
什么是 Task?
Task
是一个表示异步操作的对象。它可以返回一个结果(通过 Task<TResult>
),也可以不返回结果(通过 Task
)。Task
的主要目的是让你能够在不阻塞主线程的情况下执行耗时操作。
创建 Task
你可以通过多种方式创建 Task
,最常见的方式是使用 Task.Run
方法。以下是一个简单的示例:
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
Task<int> task = Task.Run(() => {
// 模拟耗时操作
Task.Delay(1000).Wait();
return 42;
});
int result = await task;
Console.WriteLine(result); // 输出: 42
}
}
在这个示例中,Task.Run
创建了一个新的 Task
,它会在后台线程中执行一个耗时操作,并返回一个整数结果。通过 await
关键字,我们可以等待 Task
完成并获取其结果。
Task 的状态
Task
有几种不同的状态,包括:
- Created:
Task
已创建但尚未开始执行。 - Running:
Task
正在执行。 - RanToCompletion:
Task
已成功完成。 - Faulted:
Task
因未处理的异常而失败。 - Canceled:
Task
被取消。
你可以通过 Task.Status
属性来检查 Task
的当前状态。
Task task = Task.Run(() => {
Task.Delay(1000).Wait();
});
Console.WriteLine(task.Status); // 输出: Running
task.Wait();
Console.WriteLine(task.Status); // 输出: RanToCompletion
等待 Task 完成
你可以使用 await
关键字或 Task.Wait
方法来等待 Task
完成。await
是推荐的方式,因为它不会阻塞主线程。
Task task = Task.Run(() => {
Task.Delay(1000).Wait();
});
await task; // 等待 Task 完成
Console.WriteLine("Task 已完成");
处理 Task 的异常
如果 Task
在执行过程中抛出异常,你可以通过 try-catch
块来捕获并处理这些异常。
Task task = Task.Run(() => {
throw new InvalidOperationException("发生了错误");
});
try
{
await task;
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message); // 输出: 发生了错误
}
实际案例
案例 1: 并行下载多个文件
假设你需要从网上下载多个文件,并且希望并行执行这些下载任务以提高效率。你可以使用 Task
来实现这一点。
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
string[] urls = {
"https://example.com/file1.txt",
"https://example.com/file2.txt",
"https://example.com/file3.txt"
};
Task[] downloadTasks = new Task[urls.Length];
for (int i = 0; i < urls.Length; i++)
{
downloadTasks[i] = DownloadFileAsync(urls[i]);
}
await Task.WhenAll(downloadTasks);
Console.WriteLine("所有文件已下载完成");
}
static async Task DownloadFileAsync(string url)
{
using (HttpClient client = new HttpClient())
{
string content = await client.GetStringAsync(url);
Console.WriteLine($"已下载: {url}");
}
}
}
在这个示例中,我们使用 Task.WhenAll
来等待所有下载任务完成。这样可以确保所有文件都下载完成后才继续执行后续代码。
案例 2: 计算密集型任务
如果你有一个计算密集型任务,你可以使用 Task.Run
将其放在后台线程中执行,以避免阻塞主线程。
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
Task<int> task = Task.Run(() => {
int result = 0;
for (int i = 0; i < 1000000; i++)
{
result += i;
}
return result;
});
int result = await task;
Console.WriteLine($"计算结果: {result}");
}
}
在这个示例中,我们使用 Task.Run
将一个计算密集型任务放在后台线程中执行,并通过 await
等待其完成。
总结
Task
是 C# 异步编程的核心概念之一,它允许你在不阻塞主线程的情况下执行耗时操作。通过 Task.Run
、await
和 Task.WhenAll
等方法,你可以轻松地管理异步操作的状态、结果和异常。
在实际应用中,Task
可以用于并行下载文件、执行计算密集型任务等场景。掌握 Task
的基础知识将帮助你编写高效、响应迅速的应用程序。
附加资源
练习
- 编写一个程序,使用
Task.Run
并行计算 1 到 1000000 的和,并输出结果。 - 修改案例 1 中的代码,使其在下载文件时显示下载进度。
- 尝试使用
Task.WhenAny
来等待第一个完成的任务,并输出其结果。
在编写异步代码时,务必注意异常处理和资源管理,以避免潜在的内存泄漏和未处理的异常。