c# 学习笔记 - 异步编程

本文详细介绍了C#中的异步编程,包括async/await关键字的使用、Task和Task<TResult>对象的应用,以及如何利用异步处理并发任务和控制回调行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 异步编程介绍

1.1 简单介绍

  异步编程官方参考文档:异步编程

1.2 async/await 使用

  细节注意

  1. async 用来修饰方法,表示这个方法可以成为一个异步方法,但是如果内部没有搭配使用 await 关键字的话其作用还是等效于一个同步方法
  2. await 关键字必须用于在 async 修饰的异步方法内使用,await 的作用就是立即返回调用方法的结果,外部调用该 async 方法的地方将不会等待这个 await 关键字修饰 方法/其他延时 操作的完成 (拆分出主线程和子线程,子线程继续负责被调用函数执行,主线程遇到await 返回上一层线程当中)
    但这个async 异步方法内,下面的操作还是必须要等待这个 await 关键字修饰 方法/其他延时 操作的完成.
class Program {
    static void Main(string[] args) {
        Console.WriteLine("This is methof(Main) starting ...");
        Test();
        Console.WriteLine("This is methof(Main) ending ...");
        Console.ReadLine(); // 等待输入(卡住主线程, 不让主线程先结束)
    }

    static async void Test() {
        Console.WriteLine("This is methof(Test) starting ...");
        // 立即返回Test函数运行结果,继续Main函数运行. 并且同时会进行延时操作后再继续下面的方法.
        await Task.Delay(3000);
        Console.WriteLine("This is methof(Test) ending ...");
    }
}
/*
This is methof(Main) starting ...
This is methof(Test) starting ...
// await Task.Delay(3000); 返回主线程运行 + 当前Test()方法此位置暂停运行3s
This is methof(Main) ending ... 
This is methof(Test) ending ...
*/

1.3 Task/Task 对象

  简单说明

  1. 使用Task或Task对象来保存正在运行的任务, 可以使用这个对象来保存一个正在运行的任务.
class Program {
    static void Main(string[] args) {
        Task<int> task = Task.Run(() => {
            Console.WriteLine("Hello world ..");
            return 2024;
        });

        int sum = task.Result;
        Console.WriteLine(sum);
    }
}
/*
Hello world ..
2024
*/

1.4 细节

  简单说明

  1. Main方法使用异步需要 static async Task Main(string[] args)
  2. 父线程必须对子线程的执行结果进行保证,可以在父线程启动多个子线程 或者 只启动一个子线程,但这些线程的结果必须环环相扣,如果不使用 await 去保证线程之间调用结果的回调则有可能出现 父线程执行完成时子线程还未完成(这样就会导致本应子线程处理的任务被强行关闭).

  例如1.2 中 Console.ReadLine(); // 等待输入(卡住主线程, 不让主线程先结束)

class Program {
    static void Main(string[] args) {
        Console.WriteLine("This is methof(Main) starting ...");
        Test();
        Console.WriteLine("This is methof(Main) ending ...");
        // 注释本行代码则Main线程遇到 Test当中的 awaait就会回调执行, 会导致 Main线程早于子线程执行完毕.(子线程被强制中断)
        // Console.ReadLine(); 
    }

    static async void Test() {
        Console.WriteLine("This is methof(Test) starting ...");
        await Task.Delay(3000);
        Console.WriteLine("This is methof(Test) ending ...");
    }
}
/*
This is methof(Main) starting ...
This is methof(Test) starting ...
This is methof(Main) ending ... (主线程回调直接执行完结果)
*/

  父线程调用异步子线程时必须等待,否则在遇到子线程当中 await方法回调后若父线程执行过快则可能导致子线程操作不会执行. 例如如果想在该子线程下完成实体的新建(因为父线程未进行等待,此新建操作压根不会执行)
  这里还有一个关键点,当使用异步编程去优化主线程,提高应用的响应性和性能时需要确保底层系统交互时的服务/接口是支持异步的,只有其支持异步才能在本地完成主线程的释放操作例如 如果将以下示例代码当中的 var resp = (ExecuteMultipleResponse) await service.ExecuteAsync(requestWithResults); 这一行代码替换成一些同步函数 service.Create(xxx) 的话这里是无法支持异步编程的.

class Program {
    static string connectionString = $"""""";

    static async Task Main(string[] args) {
        Console.WriteLine("This is methof(Main) starting ...");
        ServiceClient service = new ServiceClient(connectionString);
        Test(service); // 这里父线程没有进行等待子异步任务
        Console.WriteLine("This is methof(Main) ending ...");
    }

    static async Task Test(ServiceClient service) {
        Console.WriteLine("This is methof(Test) starting ...");
        await Task.Delay(3000); // 遇到await父线程回调, 因为父线程执行过快,下面的新建实体操作则压根不会执行
        Console.WriteLine("子线程无法执行到此位置");

        OrganizationRequestCollection requests = new OrganizationRequestCollection();
        for (int i = 0; i < 3; i++) {
            Entity x = new Entity("new_response");
            x["new_question001"] = "satified" + "hhhh" + i.ToString();
            requests.Add(new CreateRequest(){ Target = x});

        }
        
        var requestWithResults = new ExecuteMultipleRequest() {
            Settings = new ExecuteMultipleSettings() {
                ContinueOnError = true,
                ReturnResponses = true
            },
            Requests = requests
        };
        var resp = (ExecuteMultipleResponse) await service.ExecuteAsync(requestWithResults);

        Console.WriteLine(resp);
    }
}

2. 样例

2.1 主线程启动多个任务,需要等待结果时再 await 所有任务 Task.WhenALL

等待多个线程任务执行完成

    class Program {
        static async Task Main(string[] args) {
            int[] nums = { 12, 22, 32, 42, 52 };
            List<Task> tasks = new List<Task>();

            for(int i = 0; i < nums.Length; i++ ) {
                tasks.Add(RunAsyncTask(nums[i]));
            }

            await Task.WhenAll(tasks); // 等待所有任务(大概3s即可执行这5个任务)
        }

        static async Task RunAsyncTask(int k) {
            Console.WriteLine("This is number " + k + " starting.... ");
            await Task.Delay(3000);
            Console.WriteLine("This is number " + k + " ending.... ");
        }
    }
/*
This is number 12 starting....
This is number 22 starting....
This is number 32 starting....
This is number 42 starting....
This is number 52 starting....
# 大约3s 等待时间 #
This is number 42 ending....
This is number 32 ending....
This is number 52 ending....
This is number 12 ending....
This is number 22 ending....
*/

2.2 遇到 await 方法会分出两个线程,一个继续当前线程执行,另一个会返回调用线程(但是如果调用线程本身用await修饰,则调用线程依旧会卡着)

class Program {
    // 执行Main 方法为 Main线程
    static void Main(string[] args) {
        Test(); // Main线程进入其中
        Console.WriteLine("This is Main Finish  .....");
        Console.ReadLine(); // 这里是为了卡着Main线程,不让Main线程结束(否则看不到其他线程结果)
    }

    // 执行Test 方法为 Test线程
    static async void Test() {
        await playGameAsync(); // Main 线程遇到 await, 会拆分出 Test线程. 此时, Main线程回调到Main方法,Test线程继续执行 playGameAsync()方法
        Console.WriteLine("==========================================================");
        Console.WriteLine("i am washing right now ...");
    }

    // 执行 playGameAsync 为 play线程
    static async Task playGameAsync() {
        await Task.Run(() => { // Test 线程来这也会拆分两个线程(但是play线程回调时因为自己被await修饰了, 所以不能像Main线程一样立即向下执行),需要等待 play线程执行完成
            Task.Delay(3000).Wait();
            Console.WriteLine("I am play game ..");
        });
        Task.Delay(3000).Wait();
        Console.WriteLine("i am finish play game");
    }
}
/*
This is Main Finish  .....(等待3s)
I am play game ..
i am finish play game  (明显发现 finish play game 的输出顺序会优先于 ====== )
==========================================================
i am washing right now ...
*/

2.3 业务使用异步编程 -> 需要确保与CRM 系统进行大量数据交互或复杂业务逻辑处理时其相关接口/函数是支持异步操作的 (确保其环境支持异步操作)

详细案例可以见上述 1.4 案例当中的新建操作,如果替换成为 OrganizationServi.Create() 操作则会导致其异步编程失效,
因为这个数据交互的接口是同步接口,无法支持异步相关操作.

3. 补充拓展说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

psudd

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

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

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

打赏作者

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

抵扣说明:

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

余额充值