跳到主要内容

C# Task 基础

介绍

在 C# 中,Task 是异步编程的核心概念之一。它代表一个异步操作,允许你在不阻塞主线程的情况下执行耗时操作。通过使用 Task,你可以编写高效、响应迅速的应用程序,尤其是在处理 I/O 操作、网络请求或计算密集型任务时。

TaskSystem.Threading.Tasks 命名空间的一部分,它提供了一种简单的方式来管理异步操作的状态、结果和异常。

Task 的基本概念

什么是 Task?

Task 是一个表示异步操作的对象。它可以返回一个结果(通过 Task<TResult>),也可以不返回结果(通过 Task)。Task 的主要目的是让你能够在不阻塞主线程的情况下执行耗时操作。

创建 Task

你可以通过多种方式创建 Task,最常见的方式是使用 Task.Run 方法。以下是一个简单的示例:

csharp
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 的当前状态。

csharp
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 是推荐的方式,因为它不会阻塞主线程。

csharp
Task task = Task.Run(() => {
Task.Delay(1000).Wait();
});

await task; // 等待 Task 完成
Console.WriteLine("Task 已完成");

处理 Task 的异常

如果 Task 在执行过程中抛出异常,你可以通过 try-catch 块来捕获并处理这些异常。

csharp
Task task = Task.Run(() => {
throw new InvalidOperationException("发生了错误");
});

try
{
await task;
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message); // 输出: 发生了错误
}

实际案例

案例 1: 并行下载多个文件

假设你需要从网上下载多个文件,并且希望并行执行这些下载任务以提高效率。你可以使用 Task 来实现这一点。

csharp
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 将其放在后台线程中执行,以避免阻塞主线程。

csharp
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.RunawaitTask.WhenAll 等方法,你可以轻松地管理异步操作的状态、结果和异常。

在实际应用中,Task 可以用于并行下载文件、执行计算密集型任务等场景。掌握 Task 的基础知识将帮助你编写高效、响应迅速的应用程序。

附加资源

练习

  1. 编写一个程序,使用 Task.Run 并行计算 1 到 1000000 的和,并输出结果。
  2. 修改案例 1 中的代码,使其在下载文件时显示下载进度。
  3. 尝试使用 Task.WhenAny 来等待第一个完成的任务,并输出其结果。
提示

在编写异步代码时,务必注意异常处理和资源管理,以避免潜在的内存泄漏和未处理的异常。