前言
本节主要介绍异步编程中Task、Async和Await的基础知识。
什么是异步?
异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
异步和多线程
相同点:避免调用线程阻塞,从而提高软件的可响应性。
不同点:
异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能。C#5.0 .NET4.5 以后关键字Async和Await的使用,使得异步编程变得异常简单。
多线程中的处理程序依然是顺序执行,但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。
异步应用场景及原理
异步主要应用于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP网络通信
原因:对于IO操作并不需要CPU进行过多的计算,这些数据主要通过磁盘进行处理,如果进行同步通信无法结束,需要创建更多的线程资源,线程的数据上下文频繁的切换也是对资源的浪费,针对IO操作不需要单独的分配一个线程来处理。
举例说明:
操作:服务器接收HTTP请求对数据库进行操作然后返回
同步处理请求的线程会被阻塞,异步处理请求的线程不会阻塞。
任务
在使用任务之前,针对线程的调用大多都用线程池提供的静态方法QueueUserWorkItem,但是这个函数有很多的限制,其中最大的问题就是没有内部机制可以让开发者知道操作在什么时候完成,也没有机制在操作完成时获取返回值,微软为了解决这个问题引入了任务的概念。
首先构造一个Task<TResult>对象,并为TResult传递返回值,开始任务之后等待它并回去结果,示例代码:
static void Main(string[] args) { Console.WriteLine("开始进行计算"); // ThreadPool.QueueUserWorkItem(Sum, 10); Task<int> task = new Task<int>(Sum, 100); task.Start(); //显示等待获取结果 task.Wait(); //调用Result时,等待返回结果 Console.WriteLine("程序结果为 Sum = {0}",task.Result); 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); sum += j; } Console.WriteLine( " = {0}",sum); 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<int>(() => Sum(ctx.Token, 100000)); task.Start(); //显示等待获取结果 //task.Wait(ctx.Token); Thread.Sleep(1000); ctx.Cancel(); //调用Result时,等待返回结果 Console.WriteLine("程序结果为 Sum = {0}", task.Result); 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); sum += j; } Console.WriteLine(" = {0}", sum); return sum; }
任务完成后自动启动新任务
实际的开发应用中,经常出现一次任务完成后立刻启动另外一个任务,并且不能够使线程阻塞,在任务尚未完成时调用result会使程序阻塞,无法查看任务的执行进度,TASK提供了一个方法ContinueWith,它不会阻塞任何线程,当第一个任务完成时,会立即启动第二个任务。
static void Main(string[] args) { Console.WriteLine("开始进行计算"); // ThreadPool.QueueUserWorkItem(Sum, 10); var ctx = new CancellationTokenSource(); var task = new Task<int>(() => Sum(ctx.Token, 100000)); task.Start(); var cwt = task.ContinueWith(p => { Console.WriteLine("task result ={0} ",task.Result); }); //显示等待获取结果 //task.Wait(ctx.Token); Thread.Sleep(1000); ctx.Cancel(); //调用Result时,等待返回结果 Console.WriteLine("程序结果为 Sum = {0}", task.Result); 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); sum += j; } Console.WriteLine(" = {0}", sum); 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<TResult>返回值的函数进行异步处理。
示例目的:获取网址JS代码,并在界面显示。
private static async Task<string> DownloadStringWithRetries(string uri) { 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代码,这是因为异步开启了新的线程,并不会造成线程阻塞。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持小牛知识库!
本文向大家介绍C#编程总结(六)详解异步编程,包括了C#编程总结(六)详解异步编程的使用技巧和注意事项,需要的朋友参考一下 1、什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。 2、同步与异步的区别 同步(Synchronous
本文向大家介绍Javascript异步编程async实现过程详解,包括了Javascript异步编程async实现过程详解的使用技巧和注意事项,需要的朋友参考一下 async官方DOC 介绍 node安装 npm install async --save 使用 var async = require('async') js文件 https://github.com/caolan/async/tre
介绍 Javascript 是一个单线程的编程语言,单线程的特点就是一次只能处理一件事情,当前代码任务耗时执行会阻塞后续代码的执行。异步编程则是一种事件驱动编程,请求调用函数或方法后,无需立即等待响应,可以继续执行其他任务,而之前任务响应返回后可以通过状态、通知和回调来通知调用者。 异步编程方法 js 中的异步编程方法有回调函数、事件处理函数、观察者、Promise、Generator、async
NodeJS最大的卖点——事件机制和异步IO,对开发者并不是透明的。开发者需要按异步方式编写代码才用得上这个卖点,而这一点也遭到了一些 NodeJS反对者的抨击。但不管怎样,异步编程确实是NodeJS最大的特点,没有掌握异步编程就不能说是真正学会了NodeJS。本章将介绍与异步编 程相关的各种知识。 回调 在代码中,异步编程的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异
目前为止,我们在做的都是同步编程。同步编程执行过程很简单:一个程序从第一行开始,逐行执行一直到末尾。每次调用一个函数时,程序就会等待这个函数返回然后在执行下一行。 在异步编程中,函数地执行通常是非阻塞的。换句话说,每次你调用一个函数它就会立即返回,但相对得,这就表示函数并不会立即被执行。它有了一种机制(名为 调度程序),让可以随时在未来执行这些函数。 使用异步编程会导致程序在任何异步函数开始之前就
本文向大家介绍C#实现异步编程的方法,包括了C#实现异步编程的方法的使用技巧和注意事项,需要的朋友参考一下 最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很有必要好好地学习一下C#异步编程。 什么是异步 异步指的就是不用阻塞当前线程来等待任务的完成,而是将任务扔到线程池中去执行,当前线程可以继续向下执行,直至其它线程将任务完成,并回调通知当前线程。整个任务从开始到结束都是