keywords: 异步编程, Task, async 等待, 并行编程。


在现代编程中,异步编程(Asynchronous Programming)是一项关键技术,能够显著提升应用程序的响应速度和性能。本章将深入探讨异步编程的概念,以及如何在 C#中使用 Task、Task以及 async 和 await 关键字,最后还会介绍并行编程的实现。

什么是异步编程

异步编程是一种编程模式,允许程序在等待一个操作完成时继续执行其他操作,而不会被阻塞。这种方式特别有用在 I/O 操作(如文件读写、网络请求)或长时间运行的计算任务中。

同步 vs 异步

同步编程:在同步编程中,任务是按顺序执行的,一个任务完成后才会开始下一个任务。如果某个任务需要等待很长时间(例如读取一个大文件),程序其余部分将被阻塞,直到该任务完成。

异步编程:在异步编程中,程序可以在等待任务完成时继续执行其他任务。这使得程序在执行 I/O 密集型或耗时任务时更加高效。

  1. sequenceDiagram
  2. participant Synchronous
  3. participant Task1
  4. participant Task2
  5. Synchronous->>Task1: 开始
  6. Task1-->>Synchronous: 完成
  7. Synchronous->>Task2: 开始
  8. Task2-->>Synchronous: 完成
  1. sequenceDiagram
  2. participant Asynchronous
  3. participant Task1
  4. participant Task2
  5. Asynchronous->>Task1: 开始
  6. activate Task1
  7. Asynchronous->>Task2: 开始
  8. activate Task2
  9. Task1-->>Asynchronous: 完成
  10. deactivate Task1
  11. Task2-->>Asynchronous: 完成
  12. deactivate Task2

Task 和 Task

在 C#中,Task 和 Task是实现异步编程的基础。

Task

Task 表示一个异步操作。你可以通过 Task 的静态方法 Run 来启动一个新的任务。

  1. using System;
  2. using System.Threading.Tasks;
  3. class Program
  4. {
  5. static void Main()
  6. {
  7. Task task = Task.Run(() =>
  8. {
  9. // 模拟一个耗时任务
  10. for (int i = 0; i < 10; i++)
  11. {
  12. Console.WriteLine("Task running...");
  13. Task.Delay(1000).Wait();
  14. }
  15. });
  16. task.Wait(); // 等待任务完成
  17. }
  18. }

Task

Task表示一个返回值的异步操作。例如,你可以启动一个 Task,它将在完成时返回一个字符串。

  1. using System;
  2. using System.Threading.Tasks;
  3. class Program
  4. {
  5. static void Main()
  6. {
  7. Task<string> task = Task.Run(() =>
  8. {
  9. Task.Delay(2000).Wait();
  10. return "Task completed!";
  11. });
  12. task.Wait(); // 等待任务完成
  13. Console.WriteLine(task.Result); // 输出任务结果
  14. }
  15. }

async 和 await 关键字

使用 async 和 await 关键字可以简化异步代码的编写,使代码更易读和维护。

定义异步方法

在方法前加上 async 关键字,表示该方法是异步方法。异步方法通常返回 Task 或 Task。

  1. using System;
  2. using System.Threading.Tasks;
  3. class Program
  4. {
  5. static async Task Main()
  6. {
  7. string result = await PerformTask();
  8. Console.WriteLine(result);
  9. }
  10. static async Task<string> PerformTask()
  11. {
  12. await Task.Delay(2000); // 模拟异步操作
  13. return "Task completed!";
  14. }
  15. }

使用 await 等待异步操作

在异步方法中,使用 await 关键字等待异步操作完成。await 会暂停方法的执行,直到异步操作完成,然后继续执行后续代码。

  1. static async Task PerformMultipleTasks()
  2. {
  3. Task task1 = Task.Delay(2000);
  4. Task task2 = Task.Delay(3000);
  5. await task1;
  6. Console.WriteLine("Task 1 completed");
  7. await task2;
  8. Console.WriteLine("Task 2 completed");
  9. }

异步方法的错误处理

在异步方法中使用 try-catch 块,可以捕获异步操作中的异常。

  1. static async Task PerformTaskWithErrorHandling()
  2. {
  3. try
  4. {
  5. await Task.Run(() =>
  6. {
  7. throw new InvalidOperationException("Something went wrong");
  8. });
  9. }
  10. catch (Exception ex)
  11. {
  12. Console.WriteLine($"Exception caught: {ex.Message}");
  13. }
  14. }

并行编程

并行编程(Parallel Programming)是一种并发编程模型,允许程序同时执行多个操作。C#提供了 Parallel 类来支持并行编程。

Parallel.For

Parallel.For 方法用于并行执行一个 for 循环。

  1. using System;
  2. using System.Threading.Tasks;
  3. class Program
  4. {
  5. static void Main()
  6. {
  7. Parallel.For(0, 10, i =>
  8. {
  9. Console.WriteLine($"Task {i} is running on thread {Task.CurrentId}");
  10. Task.Delay(1000).Wait();
  11. });
  12. }
  13. }

Parallel.ForEach

Parallel.ForEach 方法用于并行执行对集合中的每个元素的操作。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. class Program
  5. {
  6. static void Main()
  7. {
  8. List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
  9. Parallel.ForEach(numbers, number =>
  10. {
  11. Console.WriteLine($"Processing number {number} on thread {Task.CurrentId}");
  12. Task.Delay(1000).Wait();
  13. });
  14. }
  15. }

并行编程的注意事项

尽管并行编程能够显著提升程序性能,但需要注意以下事项:

  • 线程安全:确保并行执行的代码是线程安全的,避免数据竞争和死锁等问题。
  • 资源竞争:并行任务可能会争夺系统资源,如 CPU 和内存,合理分配资源。
  • 调试难度:并行编程可能会增加代码的调试难度,注意日志记录和错误处理。

本章介绍了异步编程的基本概念,以及如何在 C#中使用 Task、Task、async 和 await 关键字实现异步操作。我们还探讨了并行编程的实现方式以及一些注意事项。通过异步和并行编程,可以显著提升应用程序的响应速度和性能,让你的程序更高效、更健壮。