前⾔
本节主要介绍异步编程中Task、Async和Await的基础知识。什么是异步?
异步处理不⽤阻塞当前线程来等待处理完成,⽽是允许后续操作,直⾄其它线程将处理完成,并回调通知此线程。异步和多线程
相同点:避免调⽤线程阻塞,从⽽提⾼软件的可响应性。不同点:
异步操作⽆须额外的线程负担,并且使⽤回调的⽅式进⾏处理,在设计良好的情况下,处理函数可以不必使⽤共享变量(即使⽆法完全不⽤,最起码可以减少 共享变量的数量),减少了死锁的可能。C#5.0 .NET4.5 以后关键字Async和Await的使⽤,使得异步编程变得异常简单。
多线程中的处理程序依然是顺序执⾏,但是多线程的缺点也同样明显,线程的使⽤(滥⽤)会给系统带来上下⽂切换的额外负担。并且线程间的共享变量可能造成死锁的出现。异步应⽤场景及原理
异步主要应⽤于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP⽹络通信
原因:对于IO操作并不需要CPU进⾏过多的计算,这些数据主要通过磁盘进⾏处理,如果进⾏同步通信⽆法结束,需要创建更多的线程资源,线程的数据上下⽂频繁的切换也是对资源的浪费,针对IO操作不需要单独的分配⼀个线程来处理。举例说明:
操作:服务器接收HTTP请求对数据库进⾏操作然后返回同步处理请求的线程会被阻塞,异步处理请求的线程不会阻塞。
任务
在使⽤任务之前,针对线程的调⽤⼤多都⽤线程池提供的静态⽅法QueueUserWorkItem,但是这个函数有很多的限制,其中最⼤的问题就是没有内部机制可以让开发者知道操作在什么时候完成,也没有机制在操作完成时获取返回值,微软为了解决这个问题引⼊了任务的概念。
⾸先构造⼀个Task static void Main(string[] args) { Console.WriteLine(\"开始进⾏计算\"); // ThreadPool.QueueUserWorkItem(Sum, 10); Task //显⽰等待获取结果 task.Wait(); //调⽤Result时,等待返回结果 Console.WriteLine(\"程序结果为 Sum = {0}\ Console.WriteLine(\"程序结束\"); Console.ReadLine(); } public static int Sum(object i) { var sum = 0; for (var j = 0; j <= (int) i; j++) { Console.Write(\"{0} + \ sum += j; } Console.WriteLine( \" = {0}\ return sum; } 除了wait等待单个任务外,task还提供了等待多个任务,WaitAny和WaitAll,它阻⽌调⽤线程,直到数组中所有的Task对象完成。取消任务 任务的取消同样使⽤的是.NET Framework的标准取消操作模式,⾸先需要创建⼀个CancellationTokenSource对象,然后在函数中加⼊参数CancellationToken,将CancellationTokenSource的Token传递给⽅法,然后调⽤IsCancellationRequested获取是否已经取消该值进⾏判断。 static void Main(string[] args) { Console.WriteLine(\"开始进⾏计算\"); // ThreadPool.QueueUserWorkItem(Sum, 10); var ctx = new CancellationTokenSource(); var task = new Task //显⽰等待获取结果 //task.Wait(ctx.Token); Thread.Sleep(1000); ctx.Cancel(); //调⽤Result时,等待返回结果 Console.WriteLine(\"程序结果为 Sum = {0}\ Console.WriteLine(\"程序结束\"); Console.ReadLine(); } public static int Sum(CancellationToken cts, object i) { var sum = 0; for (var j = 0; j <= (int)i; j++) { if (cts.IsCancellationRequested) return sum; Thread.Sleep(50); Console.Write(\"{0} + \ sum += j; } Console.WriteLine(\" = {0}\ return sum; } 任务完成后⾃动启动新任务 实际的开发应⽤中,经常出现⼀次任务完成后⽴刻启动另外⼀个任务,并且不能够使线程阻塞,在任务尚未完成时调⽤result会使程序阻塞,⽆法查看任务的执⾏进度,TASK提供了⼀个⽅法ContinueWith,它不会阻塞任何线程,当第⼀个任务完成时,会⽴即启动第⼆个任务。 static void Main(string[] args) { Console.WriteLine(\"开始进⾏计算\"); // ThreadPool.QueueUserWorkItem(Sum, 10); var ctx = new CancellationTokenSource(); var task = new Task var cwt = task.ContinueWith(p => { Console.WriteLine(\"task result ={0} \ }); //显⽰等待获取结果 //task.Wait(ctx.Token); Thread.Sleep(1000); ctx.Cancel(); //调⽤Result时,等待返回结果 Console.WriteLine(\"程序结果为 Sum = {0}\ Console.WriteLine(\"程序结束\"); Console.ReadLine(); } public static int Sum(CancellationToken cts, object i) { var sum = 0; for (var j = 0; j <= (int)i; j++) { if (cts.IsCancellationRequested) return sum; Thread.Sleep(50); Console.Write(\"{0} + \ sum += j; } Console.WriteLine(\" = {0}\ return sum; } Async&Await 简单使⽤ 使⽤Async&Await的主要⽬的是⽅便进⾏异步操作,因为.net 4.0 以前进⾏异步操作时⽐较复杂的,主要是通过调⽤微软提供的异步回调⽅法进⾏编程,如果遇到需要⾃⼰实现的⽅法显得⾮常头疼,.net的各个版本都有⾃⼰主推的技术,像.NET1.1中的委托,.NET2.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.5主推的就是异步编程,⼤家只需要了解TASK+异步函数就可以实现异步编程。async:告诉CLR这是⼀个异步函数。 await:将Task private static async Task using (var client = new HttpClient()) { // 第1 次重试前等1 秒,第2 次等2 秒,第3 次等4 秒。 var nextDelay = TimeSpan.FromSeconds(1); for (int i = 0; i != 3; ++i) { try { return await client.GetStringAsync(uri); } catch { } await Task.Delay(nextDelay); nextDelay = nextDelay + nextDelay; } // 最后重试⼀次,以便让调⽤者知道出错信息。 return await client.GetStringAsync(uri); } } static void Main(string[] args) { Console.WriteLine(\"获取百度数据\"); ExecuteAsync(); Console.WriteLine(\"线程结束\"); Console.ReadLine(); } public static async void ExecuteAsync() { string text = await DownloadStringWithRetries(\"http://wwww.baidu.com\"); Console.WriteLine(text); } 运⾏结果发现,⾸先获取百度数据,线程结束,最后显⽰HTML代码,这是因为异步开启了新的线程,并不会造成线程阻塞。以上就是本⽂的全部内容,希望本⽂的内容对⼤家的学习或者⼯作能带来⼀定的帮助,同时也希望多多⽀持! 因篇幅问题不能全部显示,请点此查看更多更全内容