.net core异步编程 C# async/await Task
一、概念
异步编程是一种并发编程模型,用于在应用程序中处理长时间运行的操作,以避免阻塞主线程,提高应用程序的性能和响应能力。
同步:叫服务员点餐
异步:手机扫码点餐
服务器能同时服务的请求数量有限
增加并发量
为什么要使用:
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、异步方法不等于多线程
异步编程和多线程是两种不同的编程模型,它们有以下区别:
- 执行方式:在多线程编程中,任务通常由多个线程并发执行,每个线程独立执行任务,并且可以同时执行多个任务。而在异步编程中,任务的执行不一定需要依赖于线程,可以通过异步操作和回调机制实现任务的并发执行。
- 线程数量:在多线程编程中,我们需要显式地创建和管理多个线程,每个线程负责执行一个任务。而在异步编程中,任务的执行可以在一个或多个线程上进行,具体的线程管理和调度由底层的异步框架来处理,我们不需要显式地管理线程。
- 阻塞与非阻塞:在多线程编程中,线程执行任务时可能会出现阻塞,即一个线程在等待某个操作完成时会被挂起,等待操作完成后再继续执行。而在异步编程中,通过异步操作和回调机制,任务的执行可以是非阻塞的,即可以在等待操作完成时继续执行其他任务,而不必一直等待。
- 内存消耗:多线程编程中,每个线程都需要占用一定的内存资源,线程的创建和销毁也会带来一定的开销。而在异步编程中,由于任务的执行可以复用线程或者利用线程池,可以更有效地利用系统的资源,减少了线程创建和销毁的开销,从而节省了内存消耗。
总的来说,异步编程强调的是任务的并发执行和非阻塞操作,能够提高系统的响应性和并发性,减少资源的消耗。而多线程编程则更加注重于并行执行多个任务,利用多个线程来提高系统的处理能力。异步编程和多线程可以结合使用,例如在多线程环境下使用异步操作来提高任务的并发性和效率。
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";
}