【C#基础:第五部分 实用编程】异步编程

摘要:本文围绕C#中的异步编程展开,详细介绍了async/await原理、Task并行库、异步流(IAsyncEnumerable)以及取消令牌(CancellationToken)等关键内容。通过丰富的代码示例和详细的实操流程,帮助读者深入理解C#异步编程的概念、原理和应用场景,提升在实际项目中运用异步编程解决问题的能力。


文章目录


请添加图片描述

【C#基础:第五部分 实用编程】异步编程

关键词

C#;异步编程;async/await;Task并行库;异步流;取消令牌

一、引言

在现代软件开发中,异步编程已经成为处理I/O密集型和CPU密集型操作的重要手段。传统的同步编程在处理耗时操作时会阻塞线程,导致程序响应变慢,用户体验不佳。而异步编程允许程序在执行耗时操作时不阻塞当前线程,从而可以继续执行其他任务,提高程序的性能和响应能力。C#提供了强大的异步编程支持,包括async/await关键字、Task并行库、异步流和取消令牌等特性,使得开发者能够更加方便地实现异步操作。本文将深入探讨这些异步编程特性,帮助读者掌握C#异步编程的核心知识。

二、async/await原理

2.1 基本概念

async/await是C#中用于简化异步编程的语法糖。async关键字用于修饰方法,表示该方法是一个异步方法。异步方法通常会返回一个TaskTask<T>对象。await关键字只能在async方法内部使用,用于等待一个TaskTask<T>对象完成,并获取其结果。

2.2 工作原理

当调用一个async方法时,方法内部的代码会正常执行,直到遇到await关键字。此时,await会检查关联的Task是否已经完成。如果Task已经完成,await会立即返回Task的结果;如果Task尚未完成,await会暂停当前async方法的执行,并将控制权返回给调用者。当Task完成后,async方法会从await语句处继续执行。

2.3 代码示例

using System;
using System.Threading.Tasks;

class Program
{
   
    static async Task Main()
    {
   
        Console.WriteLine("开始执行异步方法");
        int result = await CalculateAsync();
        Console.WriteLine($"异步方法执行结果: {
     result}");
        Console.WriteLine("主线程继续执行其他任务");
    }

    static async Task<int> CalculateAsync()
    {
   
        Console.WriteLine("异步方法开始执行");
        await Task.Delay(2000); // 模拟耗时操作
        Console.WriteLine("异步方法执行完成");
        return 42;
    }
}

2.4 代码解释

  • Main方法被标记为async,并且返回Task。在Main方法中,调用了CalculateAsync方法并使用await关键字等待其结果。
  • CalculateAsync方法也是一个异步方法,它返回一个Task<int>对象。在方法内部,使用Task.Delay(2000)模拟一个耗时2秒的操作。

2.5 实操流程

  1. 打开Visual Studio,创建一个新的C#控制台应用程序项目。
  2. 将上述代码复制到Program.cs文件中。
  3. 按下F5键运行程序,观察程序的输出结果,理解async/await的工作原理。

2.6 注意事项

  • async方法不一定必须包含await关键字,但如果不包含await,该方法将以同步方式执行。
  • await关键字只能在async方法内部使用。

三、Task并行库

3.1 基本概念

Task并行库是.NET框架提供的一个用于并行编程和异步编程的强大工具。Task类表示一个异步操作,可以通过Task.Run方法在后台线程上执行一个操作,也可以使用Task.Factory.StartNew方法创建并启动一个Task

3.2 创建和启动Task

3.2.1 使用Task.Run
using System;
using System.Threading.Tasks;

class Program
{
   
    static void Main()
    {
   
        Console.WriteLine("主线程开始");
        Task task = Task.Run(() =>
        {
   
            Console.WriteLine("后台任务开始执行");
            for (int i = 0; i < 5; i++)
            {
   
                Console.WriteLine($"后台任务: {
     i}");
            }
            Console.WriteLine("后台任务执行完成");
        });

        Console.WriteLine("主线程继续执行其他任务");
        task.Wait(); // 等待任务完成
        Console.WriteLine("主线程结束");
    }
}
3.2.2 使用Task.Factory.StartNew
using System;
using System.Threading.Tasks;

class Program
{
   
    static void Main()
    {
   
        Console.WriteLine("主线程开始");
        Task task = Task.Factory.StartNew(() =>
        {
   
            Console.WriteLine("后台任务开始执行");
            for (int i = 0; i < 5; i++)
            {
   
                Console.WriteLine($"后台任务: {
     i}");
            }
            Console.WriteLine("后台任务执行完成");
        });

        Console.WriteLine("主线程继续执行其他任务");
        task.Wait(); // 等待任务完成
        Console.WriteLine("主线程结束");
    }
}

3.3 任务的状态和结果

Task类有多个属性用于表示任务的状态,如IsCompletedIsFaultedIsCanceled等。对于返回结果的任务,可以使用Task<T>类,通过Result属性获取任务的结果。

using System;
using System.Threading.Tasks;

class Program
{
   
    static void Main()
    {
   
        Console.WriteLine("主线程开始");
        Task<int> task = Task.Run(() =>
        {
   
            Console.WriteLine("后台任务开始执行");
            int sum = 0;
            for (int i = 0; i < 10; i++)
            {
   
                sum += i;
            }
            Console.WriteLine("后台任务执行完成");
            return sum;
        });

        Console.WriteLine("主线程继续执行其他任务");
        int result = task.Result; // 获取任务结果
        Console.WriteLine($"任务结果: {
     result}");
        Console.WriteLine("主线程结束");
    }
}

3.4 任务的组合

可以使用Task.WhenAllTask.WhenAny方法来组合多个任务。Task.WhenAll方法等待所有任务完成,而Task.WhenAny方法等待任何一个任务完成。

using System;
using System.Threading.Tasks;

class Program
{
   
    static async Task Main()
    {
   
        Console.WriteLine("主线程开始");
        Task<int> task1 = Task.Run(() =>
        {
   
            Console.WriteLine("任务1开始执行");
            Task.Delay(2000).Wait();
            Console.WriteLine("任务1执行完成");
            return 1;
        });

        Task<int> task2 = Task.Run(() =>
        {
   
            Console.WriteLine("任务2开始执行");
            Task.Delay(1000).Wait();
            Console.WriteLine("任务2执行完成");
            return 2;
        });

        // 等待所有任务完成
        int[] results = await Task.WhenAll(task1, task2);
        Console.WriteLine($"任务1结果: {
     results[0]}, 任务2结果: {
     results[1]}");

        // 等待任何一个任务完成
        Task<int> completedTask = await Task.WhenAny(task1, task2);
        Console.WriteLine($"第一个完成的任务结果: {
     completedTask.Result}");

        Console.WriteLine("主线程结束");
    }
}

3.5 实操流程

  1. 打开Visual Studio,创建一个新的C#控制台应用程序项目。
  2. 将上述代码示例分别复制到Program.cs文件中,依次运行每个示例,观察程序的输出结果,理解Task并行库的使用方法。

3.6 注意事项

  • 使用Task.RunTask.Factory.StartNew时,要注意线程池的使用和资源管理。
  • 在使用Task.Result属性时,如果任务尚未完成,会阻塞当前线程,建议使用await关键字异步获取结果。

四、异步流(IAsyncEnumerable)

4.1 基本概念

异步流(IAsyncEnumerable<T>)是.NET Core 3.0引入的一个新特性,用于处理异步生成的序列。与传统的IEnumerable<T>不同,IAsyncEnumerable<T>允许在生成序列元素时进行异步操作,例如从网络或文件中读取数据。

4.2 实现异步流

要实现一个异步流,需要定义一个返回IAsyncEnumerable<T>的方法,并在方法内部使用yield return关键字异步生成元素。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class Program
{
   
    static async Task Main()
    {
   
        await foreach (int number in GenerateNumbersAsync())
        {
   
            Console.WriteLine(number);
        }
    }

    static async IAsyncEnumerable<int> GenerateNumbersAsync()
    {
   
        for (int i = 0; i < 5; i++)
        {
   
            await Task.Delay(1000); // 模拟异步操作
            yield return i;
        }
    }
}

4.3 异步流的消费

使用await foreach语句来消费异步流中的元素。await foreach会自动等待每个元素的生成,并在元素可用时进行处理。

4.4 实操流程

  1. 打开Visual Studio,创建一个新的C#控制台应用程序项目。
  2. 将上述代码复制到Program.cs文件中。
  3. 按下F5键运行程序,观察程序的输出结果,理解异步流的使用方法。

4.5 注意事项

  • 异步流方法必须使用async关键字修饰,并且返回IAsyncEnumerable<T>
  • 在使用await foreach时,要确保在async方法内部使用。

五、取消令牌(CancellationToken)

5.1 基本概念

取消令牌(CancellationToken)用于在异步操作中实现取消功能。当需要取消一个正在执行的异步操作时,可以通过取消令牌向操作发送取消请求,操作可以根据取消令牌的状态来决定是否停止执行。

5.2 使用取消令牌

5.2.1 创建取消令牌源
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
   
    static async Task Main()
    {
   
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        Task task = LongRunningTaskAsync(token);

        // 模拟一段时间后取消任务
        await Task.Delay(2000);
        cts.Cancel();

        try
        {
   
            await task;
        }
        catch (OperationCanceledException)
        {
   
            Console.WriteLine("任务已取消");
        }
    }

    static async Task LongRunningTaskAsync(CancellationToken token)
    {
   
        try
        {
   
            for (int i =
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI_DL_CODE

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值