Task和async/await详解
一、什么是异步
同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行,可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类,微软极力推荐使用Task来执行异步任务,现在C#类库中的异步方法基本都用到了Task;net5.0推出了async/await,让异步编程更为方便。本篇主要介绍Task、async/await相关的内容,其他异步操作的方式会在下一篇介绍。
回到顶部回到顶部三、异步方法(async/await)
1.简单认识
在C#5.0中出现的 async和await ,让异步编程变得更简单。我们看一个获取文件内容的栗子:
class Program { static void Main(string[] args) { string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result; //调用同步方法 //string content = GetContent(Environment.CurrentDirectory + @"/test.txt"); Console.WriteLine(content); Console.ReadKey(); } //异步读取文件内容 async static TaskGetContentAsync(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); var bytes = new byte[fs.Length]; //ReadAync方法异步读取内容,不阻塞线程 Console.WriteLine("开始读取文件"); int len = await fs.ReadAsync(bytes, 0, bytes.Length); string result = Encoding.UTF8.GetString(bytes); return result; } //同步读取文件内容 static string GetContent(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); var bytes = new byte[fs.Length]; //Read方法同步读取内容,阻塞线程 int len = fs.Read(bytes, 0, bytes.Length); string result = Encoding.UTF8.GetString(bytes); return result; } }
test.txt内容是【hello world!】执行结果为:
上边的栗子也写出了同步读取的方式,将main函数中的注释去掉即可同步读取文件内容。我们可以看到异步读取代码和同步读取代码基本一致。async/await让异步编码变得更简单,我们可以像写同步代码一样去写异步代码。注意一个小问题:异步方法中方法签名返回值为Task
异步方法签名的返回值有以下三种:
① Task
② Task:如果调用方法不想通过异步方法获取一个值,仅仅想追踪异步方法的执行状态,那么我们可以设置异步方法签名的返回值为Task;
③ void:如果调用方法仅仅只是调用一下异步方法,不和异步方法做其他交互,我们可以设置异步方法签名的返回值为void,这种形式也叫做“调用并忘记”。
小结:到这里Task,async/await的简单使用已经基本结束了,一些高级特性等到工作遇到了再去研究。通过上边的介绍,我们知道async/await是基于Task的,而Task是对ThreadPool的封装改进,主要是为了更有效的控制线程池中的线程(ThreadPool中的线程,我们很难通过代码控制其执行顺序,任务延续和取消等等);ThreadPool基于Thread的,主要目的是减少Thread创建数量和管理Thread的成本。async/await Task是C#中更先进的,也是微软大力推广的特性,我们在开发中可以尝试使用Task来替代Thread/ThreadPool,处理本地IO和网络IO任务是尽量使用async/await来提高任务执行效率。