NET-10-其他-Task 异步编程教程

发布时间 2023-08-10 13:04:47作者: cactus9


前言

还是ChatGPT 牛啊,感觉以后跟着学习就够了,对于我们程序员来说,确实能提供比搜索引擎更精准的内容和参考,还有代码的参考、优化。用了都说好,下面基本上都是生成的。
我只想说程序员还要自己写笔记??????,?都不写。

常见的用法:

//异步方法的返回类型
public async Task<int> CalculateAsync()
{
    // 执行耗时操作
    await Task.Delay(1000);
    // 返回结果
    return 42;
}
//等待单个任务的完成
public async Task RunAsync()
{
    // 创建任务
    Task<int> task = CalculateAsync();
    // 等待任务的完成
    int result = await task;
    // 使用任务的结果
    Console.WriteLine("Result: " + result);
}
//等待多个任务的完成
public async Task RunAsync()
{
    // 创建多个任务
    Task<int> task1 = CalculateAsync();
    Task<int> task2 = CalculateAsync();
    // 等待所有任务的完成
    await Task.WhenAll(task1, task2);
    // 使用任务的结果
    int result1 = task1.Result;
    int result2 = task2.Result;
    Console.WriteLine("Result 1: " + result1);
    Console.WriteLine("Result 2: " + result2);
}
//任务链
public async Task RunAsync()
{
    // 创建任务链
    Task<int> task1 = CalculateAsync();
    Task<string> task2 = task1.ContinueWith(t => ProcessResultAsync(t.Result));
    // 等待最后一个任务的完成
    string result = await task2;
    Console.WriteLine("Result: " + result);
}

private async Task<string> ProcessResultAsync(int value)
{
    // 执行耗时操作
    await Task.Delay(1000);
    // 返回结果
    return "Processed result: " + value;
}


// 返回类型:
Task、Task<T>,TaskValue<T>(以避免不必要的堆分配)

//Task 类提供了一些常用的属性和方法,用于管理和操作异步任务。以下是 Task 类的一些常见属性和方法:

属性:
1. IsCompleted:获取一个值,指示任务是否已完成。
2. IsCanceled:获取一个值,指示任务是否已取消。
3. IsFaulted:获取一个值,指示任务是否因为异常而失败。
4. Result:获取任务的结果。如果任务尚未完成,访问该属性会阻塞当前线程,直到任务完成并返回结果。
5. Status:获取任务的当前状态,返回一个 TaskStatus 枚举值,表示任务的执行状态,如 TaskStatus.Running、TaskStatus.Completed、TaskStatus.Canceled 等。
6. Exception:获取一个 AggregateException 对象,该对象包含任务执行过程中抛出的所有异常。如果任务没有异常,则返回 null。
7. Id:获取任务的唯一标识符。每个任务都有一个唯一的整数标识符。


方法:
1. Wait():阻塞当前线程,直到任务完成。如果任务已经完成,该方法立即返回;否则,将一直阻塞直到任务完成。
2. Wait(TimeSpan):在指定的时间范围内阻塞当前线程,直到任务完成或超时。
3. ContinueWith(Action<Task>):指定一个操作,在任务完成后执行该操作。
4. ContinueWith(Func<Task, TResult>):指定一个操作,在任务完成后执行该操作,并返回一个表示操作结果的 Task。
5. WhenAll(IEnumerable<Task>):创建一个新的任务,该任务在提供的一组任务全部完成后完成。
6. WhenAny(IEnumerable<Task>):创建一个新的任务,该任务在提供的一组任务中的任意一个完成后完成。
7. Run(Action):创建一个任务并立即执行指定的操作。
8. Run(Func<Task>):创建一个任务并立即执行指定的异步操作。
9. FromResult<TResult>(TResult):创建一个已完成的任务,并返回指定的结果。
10. FromCanceled<TResult>(CancellationToken):创建一个已取消的任务,并关联指定的取消标记。
11. FromException<TResult>(Exception):创建一个已失败的任务,并关联指定的异常。
这些是 Task 类的一些常用属性和方法。通过这些属性和方法,可以查询任务的状态、等待任务的完成、处理任务完成后的操作,以及管理多个任务的并行执行。请根据具体的需求选择合适的属性和方法来操作和管理异步任务。

当然,我可以为你总结 Task 的一系列教程,并提供代码示例。以下是一个 markdown 格式的总结:

Task 异步编程教程

本教程将介绍 C# 中的 Task 类,它是异步编程的重要组件。通过 Task 类,你可以执行异步操作、处理异步结果、处理异常等。

目录

  1. 异步编程基础

    • 异步操作的概念和优势
    • 使用 async 和 await 关键字定义异步方法
    • 异步方法的返回类型和特点
  2. Task 类的基础

    • Task 类的构造方法和静态方法
    • Task 的状态和完成情况
    • Task 的等待和等待多个任务
  3. 异步方法和 await 关键字

    • await 关键字的作用和用法
    • 异步方法中的同步和异步行为
    • 异步方法中的异常处理
  4. 并行和并行异步操作

    • 使用 Task.WhenAll 并行执行多个异步操作
    • 使用 Task.Run 在异步代码中执行同步方法
    • 处理并行异步操作的结果和异常
  5. 取消异步操作

    • 使用 CancellationToken 实现异步操作的取消
    • 在异步方法中处理取消请求
    • 取消多个任务的执行
  6. 异步编程模式(APM)和任务基础模式(TAP)

    • APM 模式和 TAP 模式的介绍
    • 使用 Task.Factory.FromAsync 方法转换 APM 模式为 TAP 模式
    • TAP 模式的优势和推荐做法
  7. 异步编程最佳实践

    • 避免阻塞异步代码
    • 使用 ConfigureAwait(false) 提高性能
    • 错误处理和异常传播

当然,下面是按照目录生成的 markdown 格式的详细内容,包含了代码示例和说明:

1. 异步编程基础

1.1 异步操作的概念和优势

异步操作是指可以在后台进行的操作,而不会阻塞当前线程。异步操作的优势在于能够提高应用程序的响应性能,使得在执行长时间运行的操作时,不会阻塞用户界面的响应。

1.2 使用 async 和 await 关键字定义异步方法

在 C# 中,可以使用 async 和 await 关键字来定义异步方法。async 关键字用于修饰方法,并表示该方法是一个异步方法。await 关键字用于等待异步操作完成,并获取异步操作的结果。

public async Task MyAsyncMethod()
{
    // 异步操作的代码
    await Task.Delay(2000);
}

在上述示例中,我们定义了一个异步方法 MyAsyncMethod,其中使用 async 关键字修饰方法,并在方法体中使用 await 关键字等待异步操作 Task.Delay 完成。

1.3 异步方法的返回类型和特点

异步方法可以使用 Task 或 Task 作为返回类型,其中 T 是异步操作的结果类型。Task 表示一个异步操作,而 Task 表示一个异步操作,并返回一个结果。

public async Task<int> GetNumberAsync()
{
    // 异步操作的代码
    await Task.Delay(2000);
    return 42;
}

在上述示例中,我们定义了一个异步方法 GetNumberAsync,其中使用 Task 作为返回类型,并在方法体中返回一个整数结果。

异步方法具有以下特点:

  • 异步方法可以被非异步方法调用。
  • 异步方法可以调用其他异步方法,形成异步调用链。
  • 异步方法可以使用 await 关键字等待异步操作的完成。
  • 异步方法可以使用 async 关键字修饰,并在方法体中使用 await 关键字。

这些基础知识将为我们深入了解 Task 类和异步编程打下基础。

2. Task 类的基础

2.1 Task 类的构造方法和静态方法

Task 类提供了多个构造方法和静态方法来创建和管理异步操作。以下是一些常用的构造方法和静态方法:

  • Task.Run 方法:使用线程池线程执行指定的操作。
  • Task.Factory.StartNew 方法:使用指定的调度器执行指定的操作。
  • Task.FromResult 方法:创建一个已完成的任务,并提供指定的结果。
  • Task.Delay 方法:创建一个在指定时间间隔后完成的任务。

2.2 Task 的状态和完成情况

Task 类具有不同的状态来表示任务的进度和完成情况。以下是 Task 的一些常见状态:

  • TaskStatus.Created:任务已创建但尚未启

动。

  • TaskStatus.WaitingForActivation:任务已准备好但尚未被调度执行。
  • TaskStatus.Running:任务正在执行。
  • TaskStatus.WaitingToRun:任务已准备好并等待可用线程来执行。
  • TaskStatus.RanToCompletion:任务已成功完成。
  • TaskStatus.Faulted:任务因异常而失败。
  • TaskStatus.Canceled:任务因取消而中止。

可以使用 Task 的状态属性(如 Status 和 IsCompleted)来获取任务的当前状态和完成情况。

2.3 Task 的等待和等待多个任务

我们可以使用 await 关键字来等待单个任务的完成,并获取其结果。例如:

Task<int> task = GetNumberAsync();
int result = await task;

在上述示例中,我们使用 await 关键字等待异步方法 GetNumberAsync 的完成,并获取其返回的结果。

如果需要等待多个任务完成,可以使用 Task.WhenAll 或 Task.WhenAny 方法。Task.WhenAll 方法等待所有任务完成,返回一个代表所有任务的新任务。Task.WhenAny 方法等待任一任务完成,返回一个代表完成的任务。

Task<int> task1 = GetNumberAsync();
Task<int> task2 = GetAnotherNumberAsync();
Task<int[]> allTasks = Task.WhenAll(task1, task2);

await allTasks;
int[] results = allTasks.Result;

在上述示例中,我们使用 Task.WhenAll 方法等待多个任务完成,并获取所有任务的结果。

3. 异步方法和 await 关键字

3.1 await 关键字的作用和用法

await 关键字用于等待异步操作完成,并获取异步操作的结果。它可以应用于任何返回 Task 或 Task 的异步方法。

public async Task<string> GetDataAsync()
{
    // 异步操作的代码
    string data = await FetchDataAsync();
    return data;
}

在上述示例中,我们定义了一个异步方法 GetDataAsync,其中我们使用 await 关键字等待异步操作 FetchDataAsync 的完成,并获取其返回的字符串结果。

使用 await 关键字可以使当前方法在等待异步操作完成期间暂停执行,并允许其他代码继续执行。一旦异步操作完成,await 表达式将返回异步操作的结果,并恢复方法的执行。

3.2 异步方法中的同步和异步行为

异步方法中的代码可以包含同步和异步操作。在异步方法中,同步操作将按照常规的顺序执行,而异步操作将通过 await 关键字进行等待和异步执行。

public async Task PerformAsyncOperation()
{
    // 同步操作
    int result = PerformSyncOperation();

    // 异步操作
    await PerformAsyncOperation();
}

在上述示例中,我们定义了一个异步方法 PerformAsyncOperation,其中包含了一个同步操作 PerformSyncOperation 和一个异步操作 PerformAsyncOperation。异步操作通过 await 关键字进行等待和异步执行,而同步操作将按照顺序执行。

3.3 异步方法中的异常处理

异步方法中可能会发生异常,我们可以使用 try-catch 语句来捕获和处理这些异常。

public async Task PerformAsyncOperation()
{
    try
    {
        // 异步操作的代码
        await DoAsyncOperation();
        Console.WriteLine("Async operation completed successfully.");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"An error occurred: {ex.Message}");
    }
}

public async Task DoAsyncOperation()
{
    // 异步操作的代码
    await Task.Delay(2000);

    // 抛出异常
    throw new InvalidOperationException("Invalid operation");
}

在上述示例中,我们定义了一个异步方法 PerformAsyncOperation,其中使用 try-catch 语句捕获异步操作 DoAsyncOperation 中抛出的异常。在 catch 块中,我们打印错误消息来处理异常。

当异步方法中发生异常时,异常将沿着调用链向上传播,直到被捕获并处理。在异步操作的调用方,可以使用 try-catch 语句来捕获异常并执行适当的错误处理逻辑。

4. 并行和并行异步操作

4.1 使用 Task.WhenAll 并行执行多个异步操作

在某些情况下,我们希望并行执行多个异步操作,并在所有操作完成后获取结果。可以使用 Task.WhenAll 方法来实现这一目标。

public async Task<int[]> PerformParallelAsyncOperations()
{
    Task<int> task1 = GetNumberAsync();
    Task<int> task2 = GetAnotherNumberAsync();

    await Task.WhenAll(task1, task2);

    int[] results = new int[2];
    results[0] = task1.Result;
    results[1] = task2.Result;

    return results;
}

在上述示例中,我们定义了一个异步方法 PerformParallelAsyncOperations,其中创建了两个异步任务 task1task2,分别代表两个异步操作。通过调用 Task.WhenAll(task1, task2),我们等待这两个任务并行执行完成。然后,我们可以使用 task1.Resulttask2.Result 获取每个任务的结果,并将它们存储在结果数组中。

4.2 使用 Task.Run 在异步代码中执行同步方法

有时,我们可能需要在异步代码中执行一个同步方法,以便利用它的功能或性能。可以使用 Task.Run 方法来在异步环境中执行同步方法。

public async Task<string> ProcessDataAsync()
{
    string result = await Task.Run(() => ProcessData());
    return result;
}

public string ProcessData()
{
    // 同步操作的代码
    return "Processed data";
}

在上述示例中,我们定义了一个异步方法 ProcessDataAsync,其中使用 Task.Run 方法来执行同步方法 ProcessData。通过使用 lambda 表达式将同步方法作为参数传递给 Task.Run,我们可以在异步环境中执行该方法,并使用 await 关键字等待其完成。

4.3 处理并行异步操作的结果和异常

在并行异步操作中,我们可能需要处理多个任务的结果或异常。可以使用 Task.WhenAll 和 try-catch 语句来实现这一目标。

public async Task ProcessParallelAsyncOperations()
{
    Task<int> task1 = GetNumberAsync();
    Task<int> task2 = GetAnotherNumberAsync();

    Task<int[]> allTasks = Task.WhenAll(task1, task2);

    try
    {
        await allTasks;
        int[] results = allTasks.Result;
        Console.WriteLine($"Result 1: {results[0]}, Result 2: {results[1]}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"An error occurred: {ex.Message}");
    }
}

在上述示例中,我们创建了两个异步任务 task1task2,并使用 Task.WhenAll 等待它们并行执行完成。使用 try-catch 语句来捕获可能发生的异常,并在 try 块中获取任务的结果。

5. 取消异步操作

有时候,我们可能需要取消正在进行的异步操作,例如当用户主动取消操作或者超时发生时。C# 中的 Task 提供了一种机制来取消异步操作,称为 CancellationToken。

5.1 使用 CancellationToken 取消异步操作

CancellationToken 是一个结构体,用于通知异步操作是否应该被取消。在异步方法中,我们可以使用 CancellationToken 参数来接收取消请求,并在适当的时候取消操作。

public async Task<string> FetchDataAsync(CancellationToken cancellationToken)
{
    // 检查是否已请求取消操作
    cancellationToken.ThrowIfCancellationRequested();

    // 执行异步操作
    string data = await DownloadDataAsync();

    return data;
}

在上述示例中,我们定义了一个异步方法 FetchDataAsync,它接收一个 CancellationToken 参数用于接收取消请求。我们使用 cancellationToken.ThrowIfCancellationRequested() 来检查是否已请求取消操作,并在需要时抛出 OperationCanceledException。

5.2 取消异步操作

我们可以通过创建一个 CancellationTokenSource 对象,并将其 CancellationToken 属性传递给异步方法来取消异步操作。

public async Task PerformAsyncOperation(CancellationToken cancellationToken)
{
    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        try
        {
            // 启动异步操作
            Task<int> task = DoAsyncOperation(cts.Token);

            // 模拟取消操作
            await Task.Delay(1000);

            // 请求取消操作
            cts.Cancel();

            // 等待异步操作完成
            int result = await task;
            Console.WriteLine($"Result: {result}");
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation was canceled.");
        }
    }
}

public async Task<int> DoAsyncOperation(CancellationToken cancellationToken)
{
    // 检查是否已请求取消操作
    cancellationToken.ThrowIfCancellationRequested();

    // 执行异步操作
    await Task.Delay(2000, cancellationToken);

    // 返回结果
    return 42;
}

在上述示例中,我们创建了一个 CancellationTokenSource 对象,并使用 CreateLinkedTokenSource 方法将其与传入的 cancellationToken 关联起来。我们启动一个异步操作 DoAsyncOperation,并在一段时间后请求取消操作,然后等待异步操作完成。

在异步方法 DoAsyncOperation 中,我们使用 cancellationToken.ThrowIfCancellationRequested() 来检查是否已请求取消操作,并在需要时抛出 OperationCanceledException。

这样,我们就可以使用 CancellationToken 和 CancellationTokenSource 来取消异步操作。

5.3 取消多个异步操作

如果有多个异步操作需要被取消,我们可以使用 CancellationTokenSource 的 Cancel 方法来请求取消操作。

public async Task PerformMultipleAsyncOperations(CancellationToken cancellationToken)
{
    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        try
        {
            // 启动多个异步操作
            Task<int> task1 = DoAsyncOperation1(cts.Token);
            Task<int> task2 = DoAsyncOperation2(cts.Token);

            // 模拟取消操作
            await Task.Delay(1000);

            // 请求取消操作
            cts.Cancel();

            // 等待所有异步

操作完成
            await Task.WhenAll(task1, task2);

            // 获取结果
            int result1 = task1.Result;
            int result2 = task2.Result;

            Console.WriteLine($"Result 1: {result1}, Result 2: {result2}");
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation was canceled.");
        }
    }
}

public async Task<int> DoAsyncOperation1(CancellationToken cancellationToken)
{
    // 检查是否已请求取消操作
    cancellationToken.ThrowIfCancellationRequested();

    // 执行异步操作
    await Task.Delay(2000, cancellationToken);

    // 返回结果
    return 42;
}

public async Task<int> DoAsyncOperation2(CancellationToken cancellationToken)
{
    // 检查是否已请求取消操作
    cancellationToken.ThrowIfCancellationRequested();

    // 执行异步操作
    await Task.Delay(3000, cancellationToken);

    // 返回结果
    return 84;
}

在上述示例中,我们创建了一个 CancellationTokenSource 对象,并使用 CreateLinkedTokenSource 方法将其与传入的 cancellationToken 关联起来。我们启动多个异步操作 DoAsyncOperation1DoAsyncOperation2,并在一段时间后请求取消操作。然后,我们等待所有异步操作完成,并获取它们的结果。

6. 异步和同步代码的交互

在实际开发中,我们可能会遇到异步代码与同步代码之间需要进行交互的情况。C# 提供了一些机制来处理这种情况,以确保异步和同步代码能够正确地协同工作。

6.1 使用 Task.ConfigureAwait(false) 避免上下文切换

在异步代码中,当使用 await 关键字等待一个任务时,默认情况下会将代码恢复到原始的上下文中(例如 UI 线程)。然而,有时我们希望避免这种上下文切换,特别是在同步方法中调用异步方法时。可以使用 Task.ConfigureAwait(false) 来指定不恢复上下文。

public async Task<string> FetchDataAsync()
{
    HttpClient client = new HttpClient();
    string data = await client.GetStringAsync("https://example.com").ConfigureAwait(false);
    return data;
}

在上述示例中,我们在 HttpClient 的 GetStringAsync 方法上使用了 ConfigureAwait(false)。这样做可以避免恢复到原始的上下文中,从而提高异步代码的性能。

6.2 使用 Task.FromResult 返回已完成的任务

有时,我们需要在同步方法中返回一个已经完成的任务。可以使用 Task.FromResult 方法来创建一个已完成的任务,并返回其结果。

public Task<string> GetCachedDataAsync()
{
    string cachedData = GetCachedDataFromMemory();
    return Task.FromResult(cachedData);
}

在上述示例中,我们定义了一个同步方法 GetCachedDataAsync,其中我们从内存中获取了缓存的数据,并使用 Task.FromResult 方法将其封装为一个已完成的任务。

这样,我们可以在同步方法中使用异步模式,并返回一个已完成的任务。

6.3 使用 TaskCompletionSource 手动完成任务

有时,我们可能需要手动完成一个任务,而不是等待异步操作的结果。可以使用 TaskCompletionSource 来实现这一目标。

public Task<int> DelayedComputationAsync(int millisecondsDelay)
{
    var tcs = new TaskCompletionSource<int>();

    // 模拟延迟操作
    Task.Delay(millisecondsDelay).ContinueWith(_ =>
    {
        // 完成任务并返回结果
        tcs.SetResult(42);
    });

    return tcs.Task;
}

在上述示例中,我们定义了一个异步方法 DelayedComputationAsync,其中使用 TaskCompletionSource 创建了一个任务。我们使用 Task.Delay 模拟了一个延迟操作,并在延迟结束后使用 tcs.SetResult 方法来完成任务并返回结果。

这样,我们就可以手动完成一个任务,而不必等待实际的异步操作。

7. 异步代码的单元测试

在编写异步代码时,我们也需要编写相应的单元测试来验证其正确性。在 C# 中,可以使用异步测试框架来编写和执行异步单元测试。

7.1 使用 NUnit 进行异步单元测试

NUnit 是一个流行的单元测试框架,它提供了对异步测试的支持。我们可以使用 NUnit 进行异步单元测试,以确保异步代码的正确性。

首先,我们需要确保使用 async 关键字声明测试方法,并返回 TaskTask<T>。然后,我们可以在测试方法内部使用 await 关键字等待异步操作的结果,并使用断言来验证预期的结果。

[Test]
public async Task FetchDataAsync_ShouldReturnData()
{
    // 准备测试数据

    // 执行异步操作
    string result = await FetchDataAsync();

    // 验证结果
    Assert.IsNotNull(result);
    Assert.AreEqual("ExpectedData", result);
}

在上述示例中,我们定义了一个异步测试方法 FetchDataAsync_ShouldReturnData,并使用 async 关键字声明该方法。在方法体内部,我们使用 await 关键字等待 FetchDataAsync 方法的结果,并使用断言来验证预期的结果。

这样,我们就可以使用 NUnit 进行异步单元测试,并确保异步代码的正确性。

7.2 使用 Moq 框架进行异步方法的模拟

在某些情况下,我们可能需要模拟异步方法的行为,以进行单元测试。Moq 是一个流行的用于创建和管理模拟对象的框架,在异步测试中也提供了相应的支持。

我们可以使用 Moq 框架创建一个模拟对象,并设置异步方法的行为。例如,我们可以设置异步方法返回一个已完成的任务,并返回预定义的结果。

public interface IDataService
{
    Task<string> FetchDataAsync();
}

[Test]
public async Task FetchDataAsync_ShouldReturnData()
{
    // 创建模拟对象
    var mockDataService = new Mock<IDataService>();

    // 设置异步方法的行为
    mockDataService.Setup(x => x.FetchDataAsync()).ReturnsAsync("ExpectedData");

    // 使用模拟对象执行异步操作
    var result = await mockDataService.Object.FetchDataAsync();

    // 验证结果
    Assert.IsNotNull(result);
    Assert.AreEqual("ExpectedData", result);
}

在上述示例中,我们定义了一个 IDataService 接口,并使用 Moq 创建了一个模拟对象 mockDataService。然后,我们使用 Setup 方法设置了异步方法 FetchDataAsync 的行为,使其返回一个已完成的任务,并返回预定义的结果。

接下来,我们使用模拟对象的 Object 属性执行异步操作,并使用断言验证预期的结果。

这样,我们可以使用 Moq 框架进行异步方法的模拟,并对异步代码进行单元测试。

这些知识将帮助你更好地理解如

8. 总结

在本教程中,我们学习了 C# 中的 Task 类和异步编程的基础知识。我们探讨了 Task 的概念、创建和启动任务、任务的等待和结果获取,以及任务的取消。我们还介绍了异步方法和 await 关键字的使用,以及异步代码与同步代码的交互。

以下是我们在本教程中涵盖的主要内容:

  1. 异步编程基础

    • 了解异步编程的概念和优势
    • 使用 async 和 await 关键字来定义和调用异步方法
    • 使用 Task 类来表示异步操作的结果
  2. Task 类的基础

    • 创建和启动任务
    • 使用 Task.Run 方法执行异步操作
    • 使用 Task.Factory.StartNew 方法创建任务
    • 使用 Task.FromResult 返回已完成的任务
    • 使用 Task.Delay 创建延迟任务
  3. 异步方法和 await 关键字

    • 在方法签名中使用 async 关键字声明异步方法
    • 使用 await 关键字等待异步操作的完成
    • 返回 Task 或 Task 类型的任务结果
    • 使用 ConfigureAwait(false) 避免上下文切换
  4. 任务的等待和结果获取

    • 使用 await 关键字等待任务的完成
    • 使用 Task.Wait 方法同步等待任务的完成
    • 使用 Task.Result 获取任务的结果
  5. 任务的取消

    • 使用 CancellationTokenSource 和 CancellationToken 来实现任务的取消
    • 在异步操作中检查取消标记并抛出 OperationCanceledException
    • 使用 Task.WhenAny 和 Task.WhenAll 等待多个任务的完成
    • 使用 CancellationToken.Register 注册取消回调方法
  6. 异步和同步代码的交互

    • 使用 Task.ConfigureAwait(false) 避免上下文切换
    • 使用 Task.FromResult 返回已完成的任务
    • 使用 TaskCompletionSource 手动完成任务
  7. 异步代码的单元测试

    • 使用异步测试框架(如 NUnit)编写和执行异步单元测试
    • 使用模拟框架(如 Moq)模拟异步方法的行为

通过学习这些内容,你应该对 C# 中的 Task 类和异步编程有了一个扎实的基础。你可以继续深入学习和探索更高级的异步编程概念和技术,以便在实际项目中更好地应用它们。