MST

星途 面试题库

面试题:C#异步编程的深入理解与应用

C#提供了多种异步编程模型,如基于回调的异步编程、IAsyncResult模式、异步委托、Task异步编程模型以及async/await关键字。请深入分析这些异步编程模型的原理、优缺点,并结合实际场景说明何时选择使用哪种异步编程模型更为合适。同时,请编写一段复杂的异步代码,展示如何处理异步操作中的异常、取消任务以及协调多个异步任务的执行顺序。
46.5万 热度难度
编程语言C#

知识考点

AI 面试

面试题答案

一键面试

1. 基于回调的异步编程

  • 原理:将一个方法(回调函数)作为参数传递给发起异步操作的方法,当异步操作完成时,调用该回调函数。
  • 优点:简单直接,在早期的异步编程中广泛应用。
  • 缺点:代码可读性差,容易出现回调地狱(多层嵌套回调)。
  • 适用场景:简单的异步操作,并且不需要复杂的控制流程。

2. IAsyncResult模式

  • 原理:通过实现IAsyncResult接口来表示异步操作的状态,BeginXxx方法开始异步操作并返回IAsyncResult,EndXxx方法等待异步操作完成并获取结果。
  • 优点:提供了一种标准的异步编程模式,可用于多种异步场景。
  • 缺点:代码较为繁琐,需要手动管理异步状态和资源。
  • 适用场景:需要对异步操作进行更底层控制,如自定义异步操作的状态管理。

3. 异步委托

  • 原理:委托本身提供了BeginInvoke和EndInvoke方法来实现异步调用,原理类似IAsyncResult模式,但基于委托简化了实现。
  • 优点:基于委托,代码相对简洁,适合熟悉委托的开发者。
  • 缺点:同样存在代码繁琐问题,且处理复杂异步场景能力有限。
  • 适用场景:简单的异步委托调用场景,对代码简洁性有一定要求。

4. Task异步编程模型

  • 原理:Task类代表一个异步操作,通过Task.Run等方法启动异步任务,可使用await等待任务完成。
  • 优点:代码简洁,支持链式调用,方便处理多个异步任务的组合与并发。
  • 缺点:在较老的框架版本中支持性可能不足。
  • 适用场景:大多数现代异步编程场景,尤其是需要处理多个异步任务并发或顺序执行的情况。

5. async/await关键字

  • 原理:基于Task异步编程模型,async关键字标记一个异步方法,await关键字暂停异步方法的执行,直到其等待的Task完成。
  • 优点:使异步代码看起来像同步代码,极大提高了代码的可读性和可维护性。
  • 缺点:需要.NET Framework 4.5及以上版本支持。
  • 适用场景:所有适合使用Task异步编程模型的场景,尤其是对代码可读性要求高的项目。

复杂异步代码示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        try
        {
            // 启动多个异步任务
            Task task1 = Task.Run(() => LongRunningTask("Task 1", cts.Token), cts.Token);
            Task task2 = Task.Run(() => LongRunningTask("Task 2", cts.Token), cts.Token);

            // 按顺序等待任务完成
            await task1;
            await task2;

            Console.WriteLine("All tasks completed successfully.");
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("One or more tasks were cancelled.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
        }
        finally
        {
            cts.Dispose();
        }
    }

    static async Task LongRunningTask(string taskName, CancellationToken cancellationToken)
    {
        for (int i = 0; i < 10; i++)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }

            Console.WriteLine($"{taskName} is running step {i}");
            await Task.Delay(1000);
        }
    }
}

在上述代码中:

  • 异常处理:使用try - catch块捕获异步操作中可能抛出的异常,包括任务取消异常OperationCanceledException和其他一般性异常。
  • 取消任务:通过CancellationTokenSourceCancellationToken实现任务取消,在异步任务执行过程中检查CancellationToken的状态并适时抛出OperationCanceledException
  • 协调任务执行顺序:通过await关键字按顺序等待多个异步任务完成,先等待task1完成,再等待task2完成。