.net core异步编程 C# async/await Task

发布时间 2023-08-28 10:49:47作者: 不争丶

一、概念

异步编程是一种并发编程模型,用于在应用程序中处理长时间运行的操作,以避免阻塞主线程,提高应用程序的性能和响应能力。
同步:叫服务员点餐
异步:手机扫码点餐
服务器能同时服务的请求数量有限
增加并发量
为什么要使用:
1、异步编程并不是适用于所有场景。它主要适用于需要处理耗时操作、IO 操作或网络请求的情况,以避免阻塞主线程。
2、使用异步编程可以将这些耗时的操作放在后台线程或线程池中执行.,使主线程保持响应性,提高应用程序的用户体验。
3、提高并发性和吞吐量:通过异步编程,可以并发执行多个耗时操作,从而提高系统的并发性和吞吐量。
4、资源的高效利用:在同步编程中,当一个线程被阻塞等待操作完成时,它仍然会占用系统的线程资源。而异步编程利用了异步任务的特性,可以更好地利用系统的线程资源,提高系统的资源利用效率。

二、C#关键字   async 、await

异步方法:用async修饰
1、返回值一般是 Task<T>
2、无返回值,用非泛型的Task
3、调用泛型方法,方法前加上await,得到返回值 为<T>类型
4、传染性:方法中有await 调用 ,该方法修饰必须为asyns
5、调用某方法 返回值为Task,方法前加上await
6、!!! await(等待)  是同步方式写异步方法  
用 Task t =  ...Async()     就是异步方法
7、只要 类里面有 GetAwaiter() 就可以await 
????
1、调用异步方法
不加 await   没写完  就开始读
加  await   写完了  才开始读   (等待一下) 
2、编写异步方法
3.当无法使用await:
string filename = @"H:\a\1.txt";
       string s = await File.ReadAllTextAsync(filename);
//等价  
       Task<string> t = File.ReadAllTextAsync(filename);
            string s = t.Result;

     await File.WriteAllTextAsync(filename, "async hello");
//等价
     Task t=File.WriteAllTextAsync(filename, "async hello");
     t.Wait();
当我们在回调方法中使用到了异步方法,但是回调方法貌似不支持async await的写法
有2中方式 
Task.Result 和 Task.GetAwaiter.GetResult()
其实这两个使用方式是差不多的。不过,还是有一点小小的区别的:如果任务失败,Task.GetAwaiter().GetResult()会直接抛出异常,而Task.Result则会把异常包装在AggregateException中。从这个角度说Task.GetAwaiter().GetResult()要优于Task.Result。毕竟它少了异常的包装操作,即直接抛出异常,而不是把异常包装在AggregateException中。

4.委托异步
5.原理!
await、async 是“语法糖”,最终编译为“状态机调用”
总:
1、async的方法会被C#编译器编译成一个类,会主要根据await调用进行切分为多个状态,对async方法的调用会被拆分为对MoveNext的调用。
用await看似是“等待”,经过编译后,其实没有“wait”。
2、就是拆成多块,然后按顺序执行
6、async 背后的线程切换
1、遇到Await关键字,线程不会等着阻塞线程
2、Await调用    之前和之后  线程不同 (.net 会将现在线程放回线程池 等调用完后再 取一个线程 )
3、具体线程=服务员  线程可以得到充分利用
4、内容写入很少 ,线程不变
7、异步方法不等于多线程

异步编程和多线程是两种不同的编程模型,它们有以下区别:

  1. 执行方式:在多线程编程中,任务通常由多个线程并发执行,每个线程独立执行任务,并且可以同时执行多个任务。而在异步编程中,任务的执行不一定需要依赖于线程,可以通过异步操作和回调机制实现任务的并发执行。
  2. 线程数量:在多线程编程中,我们需要显式地创建和管理多个线程,每个线程负责执行一个任务。而在异步编程中,任务的执行可以在一个或多个线程上进行,具体的线程管理和调度由底层的异步框架来处理,我们不需要显式地管理线程。
  3. 阻塞与非阻塞:在多线程编程中,线程执行任务时可能会出现阻塞,即一个线程在等待某个操作完成时会被挂起,等待操作完成后再继续执行。而在异步编程中,通过异步操作和回调机制,任务的执行可以是非阻塞的,即可以在等待操作完成时继续执行其他任务,而不必一直等待。
  4. 内存消耗:多线程编程中,每个线程都需要占用一定的内存资源,线程的创建和销毁也会带来一定的开销。而在异步编程中,由于任务的执行可以复用线程或者利用线程池,可以更有效地利用系统的资源,减少了线程创建和销毁的开销,从而节省了内存消耗。

总的来说,异步编程强调的是任务的并发执行和非阻塞操作,能够提高系统的响应性和并发性,减少资源的消耗。而多线程编程则更加注重于并行执行多个任务,利用多个线程来提高系统的处理能力。异步编程和多线程可以结合使用,例如在多线程环境下使用异步操作来提高任务的并发性和效率。

8、没有async的异步方法!!!
async方法缺点:
1、异步方法会生成一个类,运行效率没有普通方法高;
2、可能会占用非常多的线程;

!!如果一个异步方法只是对别的异步方法调用的转发,并没有太多复杂的逻辑(比如等待A的结果,再调用B;把A调用的返回值拿到内部做一些处理再返回),那么就可以去掉async关键字。
9、不要用sleep()  等待
因为它会阻塞当前线程!降低并发!
不用Thread.Sleep(3000);
用 await Task.Delay(3000);
10、CancellationToken参数,用于获取提前终止执行的信号   异步中止
1、CancellationToken  结构体
None:空
bool     isCancellationRequested 是否取消
ThrowlfCancellationRequested() 如果任务被取消,执行到这句话就抛异常
2、CancellationTokenSource 类
CancelAfter()超时后发出取消信号
Cancel() 发出取消信号
3、CancellationToken cancellationToken 参数

4、手动程序中止

5、总结:方法里面有 CancellationToken 参数 记得带上!
比如控制器中api    可以自己加 

11、WhenAll   
等待 多个Task 异步执行  完成后进行操作

三、其他

1、对于接口中的方法或者抽象方法不能修饰为async
2、异步与yield:可以让数据处理“流水线化”,提升性能
  static IEnumerable<string> Test1() {
            List<string> list = new List<string>();
            list.Add("hello");
            list.Add("xw");
            return list;
        }

        static IEnumerable<string> Test2()
        {
            //一步取数据 一返回 
            yield return "HELLO";
            yield return "xw";
        }