.NET Async与Await 异步编程
前言
当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
NET 4.0 在 ThreadPool 的基础上推出了 Task 类,推荐使用Task来执行异步任务; NET 5.0 推出了 async/await,让异步编程更为方便;
C# 中的 Async 和 Await 关键字是异步编程的核心。使用这两个关键字可以轻松创建异步方法。
异步编程
并发的一种形式,它采用furture模式或回调(callback)机制,以避免产生不必要的线程。.Net中future的类型有 Task
和 Task<Result>
。
static async Task Main(string[] args) { await File.WriteAllTextAsync(@"D:\vs2019\netcode\AsyncandAwait\1.txt","hello world!!!"); Console.Read(); }
异步编程的核心理念是异步操作:
1、启动了的操作将会在一段时间后完成。
2、这个操作正在执行时,但不会阻塞原来的线程。
3、启动了这个操作的线程后,可以继续执行其它任务。
4、当操作完成时,它会通知它的future,或者调用回调函数,以便让程序知道操作已经结束。
异步的好处
1、对于面向终端用户的GUI程序,异步可以提高响应能力。
2、对于服务器应用:异步编程实现了可扩展。
3、服务器可以利用线程池填满其可扩展性,使用异步编程后,可扩展性通常可以提供一个数量级,可以最大程度的压榨服务器性能,提高处理能力。
Async
使用 async
修饰符可以将方法、lambda表达式或匿名方法指定为异步。async
的主要目的是使方法内的await关键字生效。
//等待异步完成再执行后边的操作,但是整个方法不会阻塞 var result = await DoSomethingAsync(); output.Result = result;
Await
async
标记的异步方法,可以使用 await
来指定暂定点。
执行逻辑:
1、async
方法在开始时以同步的方式执行。
2、在 async
方法内部,await
关键字对他的参数(一个异步任务)执行一个异步等待。
3、await
首先检查操作是否已经完成,如果完成了,就继续运行(同步方法)。否则,他会暂停 async
方法,并返回,将控制权交给调用方,留下一个 未完成的 Task
。一段时间后,操作完成,async
方法再恢复运行。
异步方法签名的返回值:
1、Task:如果调用方法想通过调用异步方法获取一个T类型的返回值,那么签名必须为Task;
2、Task:如果调用方法不想通过异步方法获取一个值,仅仅想追踪异步方法的执行状态,那么我们可以设置异步方法签名的返回值为Task;
3、void:如果调用方法仅仅只是调用一下异步方法,不和异步方法做其他交互,我们可以设置异步方法签名的返回值为void,这种形式也叫做“调用并忘记”。
注意事项
1、异步方法并不等于多线程,异步方法并不能在新线程中执行,除非手动放入;
2、异步方法耗时可能很高,避免可能的线程过高,占用过多资源,有的时候可以简写,如下:
static async Task<string> Main(string[] args) { string s = await readtext(1); Console.Read(); } static Task<string> readtext(int num) { if(num==1) { return File.ReadAllTextAsync(@"D:\vs2019\netcode\AsyncandAwait\1.txt"); } else { throw new ArgumentException(); } }
注意:返回值为Task<string>类型的,仅简单的转发结果,可以不使用Async和Await,可以如上方式书写。
3、如果在异步方法暂停一段时间,不要用Tread.Sleep(),因为它会阻塞调用线程,而要用await.Task.Delay();
4、需要提前终止任务,比如请求超时、用户取消请求。很多异步方法都有CancellationToken参数,用于获得提前终止执行的信号。