为何不是摘要呢?其实这个是我我的的想法,其实不少人在谈论异步与同步的时候都忽略了,同步异步不是软件的原理,其自己是计算机的原理及概念,这里就不过多的阐述计算机原理了。在学习同步与异步以前,咱们须要先研究几个问题html
在说到异步前,先来理一下几个容易混淆的概念,并行、多线程、异步。面试
并行,通常指并行计算,是说同一时刻有多条指令同时被执行,这些指令可能执行于同一CPU的多核上,或者多个CPU上,或者多个物理主机甚至多个网络中。网络
多线程,通常指同一进程中多个线程(包含其数据结构、上下文与代码片断)协做运行。在多核计算机中多个线程将有机会同时运行于多个核上,若是线程中进行的是计算,则行成并行计算。数据结构
异步,与同步相对应,是指呼叫另外一操做后,不等待其结果,继续执行以后的操做,若以后没有其余操做,当前线程将进入睡眠状态,而CPU时间将有机会切至其余线程。在异步操做完成后经过回调函数的方式获取通知与结果。异步的实现方式有多种,如多线程与完成端口。多线程将异步操做放入另外一线程中运行,经过轮询或回调方法获得完成通知;完成端口,由操做系统接管异步操做的调度,经过硬件中断,在完成时触发回调方法,此方式不须要占用额外线程。多线程
经过上面的两张图,能够把三个概念透析的很是好理解,异步在某种意义上讲是“时空转换”即时间换空间,空间换时间。下边咱们来学习下,在net 中的异步dom
为了准备一个耗时的程序,本人准备了一本Txt修仙小说,咱们用程序读取一行行输出,输出完成之后,咱们输出一句话,"今天书就读到这里吧!!累了,休息一会,休息一会!一休哥",为了更好的演示同步异步,本文采用winform程序,同时为了体验winform 和控制台 带来的视觉效果,咱们选择项目属性,应用程序这选择控制台。异步
在准备一个很费时的读书方法,async
/// <summary> /// 读书,一个很废时间的任务 /// </summary> public void ReadBook() { //咱们能够经过 Thread.CurrentThread.ManagedThreadId 获取当前线程的惟一标识符 Console.WriteLine("********************** ReadBook Start【" + Thread.CurrentThread.ManagedThreadId + "】等待............... **********************************************"); System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); string Path= AppDomain.CurrentDomain.BaseDirectory + "zqjz.txt"; List<string> list = new List<string>(); System.IO.StreamReader sr = new System.IO.StreamReader(Path, Encoding.Default); string line = ""; Console.ForegroundColor = ConsoleColor.Black; while ((line = sr.ReadLine()) != null&& list.Count<120) { char[] array= line.ToArray(); for (int i = 0; i < array.Length; i++) { Console.Write(array[i]); if (i!=0) { // Thread.Sleep(128);//人眼最快敏感视觉是128毫秒左右,咱们这里测试先使用10毫秒 Thread.Sleep(10); } } Console.WriteLine(); Console.BackgroundColor = (ConsoleColor)new Random().Next(3, 15); list.Add(line); } sr.Close(); sr.Dispose(); watch.Stop(); Console.WriteLine("今天读书用了"+ watch.ElapsedMilliseconds+"豪秒"); Console.WriteLine("********************** ReadBook End【" + Thread.CurrentThread.ManagedThreadId + " 】**********************************************"); }
这个方法比较简单,就是读取电子书,同时给方法加上了耗时记录,和当前线程的惟一标识。如今咱们在窗体上加上一个buttion 调用下咱们的读书。看看结果是怎么样的,同时建议打开任务管理器,监控下CPU,等cpu 平稳之后,咱们在点击同步执行按钮。“如今是咱们本身在读书”。函数
关于异步在前边的摘论里面介绍了大概,这里不过多演示,请继续看!在早期,net 的异步都是在使用委托来作的,而委托使用的是线程池ThreadPool来实现的,曾取下一篇文章介绍线程,到时候在详细介绍线程池,关于委托请观看本人前边的文章 "linq to Objet",咱们在程序上在加上一个按钮,里面老师读书,个人心缺飞了,在想下课玩什么?怎么和同窗玩。学习
private void btnSync_Click(object sender, EventArgs e) {//同步 Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************"); ReadBook(); MessageBox.Show("今天书就读到这里吧!!累了,休息一会,休息一会!一休哥"); Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************"); } private void btnasync_Click(object sender, EventArgs e) {//异步 Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************"); Action action = new Action(() => ReadBook()); action.BeginInvoke(null,null);//参数先无论,咱们先给null,一会咱们会继续演示 MessageBox.Show("今天想玩,怎么骗过老师呢!!书还在继续读,可是我已经在玩了!!!"); Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************"); }
上面代码分别为异步调用和同步调用,下图为异步调用结果,咱们会发现,异步调用窗体是能够移动的,而且CPU 会有很大的波峰,细心的人会发现,执行时间是同样的,只是他们的线程惟一标识是不同的。
经过上述演示,异步和同步的区别很简单了吧!这里就不过多描述,本身总结。可是咱们的要说下异步和多线程的区别?其实异步只是一种结果(目地),而多线程才是实现这种结果的一种方式,在NET 里面,异步和多线程没有本质的区别,我的总结惟一的区别就是,应用场景不一样。
重点:多播委托不能够指定异步。不予显示,本身去尝试和找寻原理,实在找不到原理能够理解为这是任何高级语言的一个规定。有关多播委托请参考本人:一步一步带你了解 Linq to Object
刚才咱们一直在上课读书,可是个人内心在想的是下课去哪里玩,如何玩?这个时候,咱们须要在异步读书的方法以后也就是下课之后再去玩。看下代码是怎么写的。
//异步 Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************"); #region 异步回调 IAsyncResult iAsyncResult = null; AsyncCallback callback = t => { Console.WriteLine(t); Console.WriteLine("下边代码是比较两个对象是否同样"); Console.WriteLine($"string.ReferenceEquals(t, iAsyncResult)={string.ReferenceEquals(t, iAsyncResult)}"); Console.WriteLine($"当前线程ID {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"终于下课了!咱们走吧,尽情的玩吧,你问我老师讲的啥,我知道!!"); };//AsyncCallback 自己就是一个委托,这个委托有一个参数,这个参数就是咱们委托的BeginInvoke的返回值。咱们使用这个委托去作异步回调 #endregion Action action = () => ReadBook();//简写 iAsyncResult= action.BeginInvoke(callback, null);//这里的第一个参数,咱们就是异步回调 MessageBox.Show("今天想玩,怎么骗过老师呢,下课玩点什么呢!!书还在继续读,可是个人心已经飞了!!!"); Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
所谓的异步回调,就是在异步线程执行完在执行的代码块。
主线程等待子线程有这么几种方式:
1.主线程等待子线程的时候有返回,好比说咱们常见的进度条功能,执行一点我就返回下。2.主线程等待子线程有时间限制,例如:中午放学我等你五分钟,你要是不完事,我就先吃饭去了。3.主线程等待子线程无返回,好比死等,今天的代码我学不会了,我就不睡觉了。下面咱们分别看看这三种状况。咱们管操做线程等待的过程叫作阻塞(se)进程.阻塞主进程之后等待子线程的执行,咱们成为线程的阻塞,
刚才咱们是使用回调,在异步执行完成,才执行了一个代码块,这个时候messagebax 已经输出了,如今咱们开看看课堂下的学生表现。“将下列代码放到咱们 MessageBox.Show("今天想玩,怎么骗过老师呢,下课玩点什么呢!!书还在继续读,可是个人心已经飞了!!!");”以后,咱们来看看
while (!iAsyncResult.IsCompleted)//边等待边操做,能够用于作进度条 { Thread.Sleep(100);//建议控制100毫秒一次 Console.WriteLine("老师还在教咱们读书.....请等待..............."); } //当异步完成之后,咱们在执行下边的这句话 Console.WriteLine("学生甲:冲啊..............打篮球全"); Console.WriteLine("学生乙:王美美.......我爱你!我们交往吧....*#*#*#**??!"); Console.WriteLine("学生丙:呼呼呼呼呼呼呼呼。。。。。噜。。。。。。。。。。今天的肉丸子真好吃,真但愿这不是梦啊"); Console.WriteLine("学生丁:大海啊,就像妈妈同样,海浪啊!你为啥这么猛!老是在我人生巅峰......被打断"); Console.WriteLine("学生丙:别BiBi了,海浪是你后妈,滚一边去淫诗去!别TMD打扰老子睡觉");
刚才执行的线程等待在阻塞的过程当中是有损耗的,咱们损耗 的是时间,因此回调会在子线程以前执行,那么咱们想要无损耗怎么去写,怎么去阻塞咱们的主线程呢 “ bool RunBool = iAsyncResult.AsyncWaitHandle.WaitOne();”; 当子线程执行成功了,就会返回TRUE,当子线程执行过程当中出现exection 之后,就返回false;
这种写法主线程就没法返回了。可是咱们能够新创建一个线程去监控子线程。这里就不写那么复杂了。
第二种状况,我只等你两秒钟,有时间限制的阻塞
#region 异步等待1 有损耗 带返回 //while (!iAsyncResult.IsCompleted)//边等待边操做,能够用于作进度条 //{ // Thread.Sleep(100);//建议控制100毫秒一次 // Console.WriteLine("老师还在教咱们读书.....请等待..............."); //} #endregion #region 异步等待2 无损耗 无返回 //bool RunBool = iAsyncResult.AsyncWaitHandle.WaitOne();//返回结果是子线程执行成功或者失败,不是实时返回的。 //iAsyncResult.AsyncWaitHandle.WaitOne(-1);//写法2 #endregion #region 有时间限制的异步等待 iAsyncResult.AsyncWaitHandle.WaitOne(2000);//我最多等你2秒钟,若是你提早完事,咱们提早走 #endregion //当异步完成之后,咱们在执行下边的这句话 Console.WriteLine("学生甲:冲啊..............打篮球全"); Console.WriteLine("学生乙:王美美.......我爱你!我们交往吧....*#*#*#**??!"); Console.WriteLine("学生丙:呼呼呼呼呼呼呼呼。。。。。噜。。。。。。。。。。今天的肉丸子真好吃,真但愿这不是梦啊"); Console.WriteLine("学生丁:大海啊,就像妈妈同样,海浪啊!你为啥这么猛!老是在我人生巅峰......被打断"); Console.WriteLine("学生丙:别BiBi了,海浪是你后妈,滚一边去淫诗去!别TMD打扰老子睡觉"); Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
这种子线程执行两秒之后,主线程在执行这个问题常常会在面试里面问。面试常常会问,主线程A 至少要执行10,秒,子线程B至少要执行30秒,如何让主线程在子线程执行20秒开始执行。
下边咱们就举例,代码不会,我就要学习了学习不会就不睡觉,就死学到底了。
#region 异步等待死等 //死等就是,只要你不异常,就必须给我一个结果,好比学习,必须学会为止 action.EndInvoke(iAsyncResult);//EndInvoke 的返回值取决与你的委托!你的委托有返回值,我就有返回值。 #endregion
注意图上反应的问题。其实回调执行的是子线程。咱们死等(阻塞 主线程等待子线程)的是子线程,而不是子线程的回调。这个时候是主线程和子线程一块儿执行的(线程的无序)。这就会照成CPU 更大的波峰,很容易宕机。因为演示这种结果不容易,须要执行不少遍,这里没有截取到CPU 波峰。本人I7 CPU 基本都赶到顶了。
经过上图能够看出,主线程和子线程的执行前后顺序不必定谁前后,线程是无序的。
若是下了本文demo 的同窗会发现,这个时候UI 是卡住的,主窗体UI阻塞,因此窗体是没法移动的。
。到这里异步咱们就学习完了,下边总结下
1.异步等待和异步回调的区别?面试会考的哦!!
答:异步等待是在子线程执行完成之后回到主线程,j解除主线程的阻塞继续执行,而异步回调是子线程执行完成之后在去以子线程再去执行的任务代码块。
异步等待卡主线程,回调不卡主线程。
在委托中回调不能够取得子线程执行的结果,等待能够经过线程状态参数取得执行结果。
2.主线程A 须要执行1秒,而子线程B须要执行3秒。若是让B执行2秒之后在执行?或者 接口A 调用5秒没结果,我就调用接口B去取数据?在接口B取到数据之后,接口若是也取到数据,仍然使用结果B的,怎么去作。
答:使用 iAsyncResult.AsyncWaitHandle.WaitOne(2000);
关于接口(WebApi ,Service)的状况,咱们也是须要使用线程等待,可是这个时候咱们就要加锁或者加计时器 StopWatch 去作。关于锁之后在谈。可是加锁会影响效率,计时器在多服务状况下还不许确,这是大多数面试者的回答。
咱们把没有演示的一点点知识在这里演示下。
咱们一直没有说这个参数有什么作用,这里简单介绍下。当我线程启动的时候,我能够启动多条线程,可是我没法肯定那个线程执行的过程,这个时候咱们能够经过这个参数传递线程状态。这里不过多解释。有用到的私聊本人。
3.若是我想使用子线程的结果去作主线程的参数,如何去作。请说明你的理由。这里不过多解释了,案列很清晰。
4.这里的阻塞是卡主线程的,咱们如何不卡主线程??
下节多线程中找答案。
我的总结:
1.net 异步支持
Net framework可让你异步调用任何方法。为达这样的目的,你能够定义一个与你要调用的方法的签名相同的委托。公共语言运行时将自动为该委托定义与签名相同的BeginInvok和EndInvoke方法。
异步委托调用BeginInvok和EndInvoke方法,但在.NET Compact Framework中并不支持。
.NET Framework 容许您异步调用任何方法。定义与您须要调用的方法具备相同签名的委托;公共语言运行库将自动为该委托定义具备适当签名
的 BeginInvoke 和 EndInvoke 方法。
BeginInvoke 方法用于启动异步调用。它与您须要异步执行的方法具备相同的参数,只不过还有两个额外的参数(将在稍后描述)。
BeginInvoke 当即返回,不等待异步调用完成。
BeginInvoke 返回 IasyncResult,可用于监视调用进度。
EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用 EndInvoke 方法;若是异步调用未完成,EndInvoke 将一直阻塞到
异步调用完成。EndInvoke 的参数包括您须要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由
BeginInvoke 返回的 IAsyncResult。
四种使用 BeginInvoke 和 EndInvoke 进行异步调用的经常使用方法。调用了 BeginInvoke 后,能够:
1.进行某些操做,而后调用 EndInvoke 一直阻塞到调用完成。
2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,而后调用
EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。
3.轮询由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted肯定异步调用什么时候完成,而后调用 EndInvoke。此处理我的认为与
相同。
4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它能够调用 EndInvoke。这是在强制装
换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,而后用委托执行EndInvoke。
警告 始终在异步调用完成后调用 EndInvoke。
经过EndInvoke方法检测异步调用的结果。若是异步调用还没有完成,EndInvoke将阻塞调用线程,直到它完成。EndInvoke参数包括out和ref参数,本文没有讲到,另外本文没有演示EndInvoke 返回值 。
同步方法调用在程序继续执行以前须要等待同步方法执行完毕返回结果
异步方法则在被调用以后当即返回以便程序在被调用方法完成其任务的同时执行其它操做