一、摘论

为什么不是摘要呢?其实这个是我个人的想法,其实很多人在谈论异步与同步的时候都忽略了,同步异步不是软件的原理,其本身是计算机的原理及概念,这里就不过多的阐述计算机原理了。在学习同步与异步之前,我们需要先研究几个问题

在说到异步前,先来理一下几个容易混淆的概念,并行、多线程、异步。

并行,一般指并行计算,是说同一时刻有多条指令同时被执行,这些指令可能执行于同一CPU的多核上,或者多个CPU上,或者多个物理主机甚至多个网络中。

多线程,一般指同一进程中多个线程(包含其数据结构、上下文与代码片段)协作运行。在多核计算机中多个线程将有机会同时运行于多个核上,如果线程中进行的是计算,则行成并行计算。

异步,与同步相对应,是指呼叫另一操作后,不等待其结果,继续执行之后的操作,若之后没有其他操作,当前线程将进入睡眠状态,而CPU时间将有机会切至其他线程。在异步操作完成后通过回调函数的方式获取通知与结果。异步的实现方式有多种,如多线程与完成端口。多线程将异步操作放入另一线程中运行,通过轮询或回调方法得到完成通知;完成端口,由操作系统接管异步操作的调度,通过硬件中断,在完成时触发回调方法,此方式不需要占用额外线程。

通过上面的两张图,可以把三个概念透析的非常好理解,异步在某种意义上讲是“时空转换”即时间换空间,空间换时间。下边我们来学习下,在net 中的异步

二、同步和异步

1.同步执行

为了准备一个耗时的程序,本人准备了一本Txt修仙小说,我们用程序读取一行行输出,输出完成以后,我们输出一句话,"今天书就读到这里吧!!累了,休息一会,休息一会!一休哥",为了更好的演示同步异步,本文采用winform程序,同时为了体验winform 和控制台 带来的视觉效果,我们选择项目属性,应用程序这选择控制台。

在准备一个很费时的读书方法,

  /// <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<)
{
char[] array= line.ToArray();
for (int i = ; i < array.Length; i++)
{ Console.Write(array[i]);
if (i!=)
{
// Thread.Sleep(128);//人眼最快敏感视觉是128毫秒左右,我们这里测试先使用10毫秒
Thread.Sleep();
}
}
Console.WriteLine();
Console.BackgroundColor = (ConsoleColor)new Random().Next(, );
list.Add(line);
}
sr.Close();
sr.Dispose();
watch.Stop();
Console.WriteLine("今天读书用了"+ watch.ElapsedMilliseconds+"豪秒");
Console.WriteLine("********************** ReadBook End【" + Thread.CurrentThread.ManagedThreadId + " 】**********************************************");
}

这个方法比较简单,就是读取电子书,同时给方法加上了耗时记录,和当前线程的唯一标识。现在我们在窗体上加上一个buttion 调用下我们的读书。看看结果是怎么样的,同时建议打开任务管理器,监控下CPU,等cpu 平稳以后,我们在点击同步执行按钮。“现在是我们自己在读书”。

2.异步执行

关于异步在前边的摘论里面介绍了大概,这里不过多演示,请继续看!在早期,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

三、异步回掉和异步等待(阻塞)

1.异步回掉:

刚才我们一直在上课读书,但是我的心里在想的是下课去哪里玩,如何玩?这个时候,我们需要在异步读书的方法之后也就是下课以后再去玩。看下代码是怎么写的。

//异步
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 + "】**********************************************");

所谓的异步回调,就是在异步线程执行完在执行的代码块。

2.异步等待(阻塞):

主线程等待子线程有这么几种方式:

1.主线程等待子线程的时候有返回,比如说我们常见的进度条功能,执行一点我就返回下。2.主线程等待子线程有时间限制,例如:中午放学我等你五分钟,你要是不完事,我就先吃饭去了。3.主线程等待子线程无返回,比如死等,今天的代码我学不会了,我就不睡觉了。下面我们分别看看这三种情况。我们管操作线程等待的过程叫做阻塞(se)进程.阻塞主进程以后等待子线程的执行,我们成为线程的阻塞,

刚才我们是使用回调,在异步执行完成,才执行了一个代码块,这个时候messagebax 已经输出了,现在我们开看看课堂下的学生表现。“将下列代码放到我们  MessageBox.Show("今天想玩,怎么骗过老师呢,下课玩点什么呢!!书还在继续读,但是我的心已经飞了!!!");”之后,我们来看看

 while (!iAsyncResult.IsCompleted)//边等待边操作,可以用于做进度条
{
Thread.Sleep();//建议控制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();//我最多等你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 返回值 。

2.同步方法和异步方法的区别

同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作

Demo 下载

下篇 多线程Thread

net 异步与同步的更多相关文章

  1. ASP.NET sync over async(异步中同步,什么鬼?)

    async/await 是我们在 ASP.NET 应用程序中,写异步代码最常用的两个关键字,使用它俩,我们不需要考虑太多背后的东西,比如异步的原理等等,如果你的 ASP.NET 应用程序是异步到底的, ...

  2. 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

    文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...

  3. C#中的异步和同步

    同步 同步(英语:Synchronization [ˌsɪŋkrənaɪ'zeɪʃn]),指对在一个系统中所发生的事件(event)之间进行协调,在时间上出现一致性与统一化的现象.说白了就是多个任务一 ...

  4. 漫话JavaScript与异步·第三话——Generator:化异步为同步

    一.Promise并非完美 我在上一话中介绍了Promise,这种模式增强了事件订阅机制,很好地解决了控制反转带来的信任问题.硬编码回调执行顺序造成的"回调金字塔"问题,无疑大大提 ...

  5. C# 异步转同步

    当我们的程序运行时,调用了一段异步的逻辑A,这段异步的逻辑无法转化为同步(如动画.下载进度等) 而,我们又需要等待异步逻辑A处理完成,然后再执行其它逻辑B. 那就迫切需要将异步转同步了! //参数bo ...

  6. nodejs异步转同步

    项目在微信环境开发,需要获取access_token进行授权登录和获取用户信息. 特意把这块功能拿出来封装一个自定义module module.exports = new Wechat(con.app ...

  7. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  8. ajax中的async属性值之同步和异步及同步和异步区别

    jquery中ajax方法有个属性async用于控制同步和异步,默认是true,即ajax请求默认是异步请求,有时项目中会用到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里 ...

  9. Java并发(二)异步转同步

    目录 前置条件:构造一个异步调用 一.使用wait和notify方法 二.使用条件锁 三.Future 四.使用CountDownLatch 五.使用CyclicBarrier 总结 在Java并发编 ...

  10. python全栈开发day31-操作系统介绍,异步、同步、阻塞、非阻塞,进程

    一.网络编程内容回顾 1.arp协议 #交换机 #广播.单播 2.ip协议 3.tcp和udp协议 tcp:可靠的,面向连接的,字节流传输,长连接 三次握手:一方发送请求,另一方确认请求同时发送请求, ...

随机推荐

  1. 完美融合 nextjs 和 antd

    相信大家在使用nextjs的时候,难免遇到一些坑.其实可能大部分原因在于 nextjs 做了很多封装,我们可能不能第一时间搞清楚包括它相关的所有配置,比如其中的webpack配置.我前面也写过 SSR ...

  2. ScintillaNET的应用

    出于工作需要,需要制作一个嵌入在桌面应用中的C语言编辑器,经过一系列调研,目前ScintillaNET应该是最合适的了,开源.轻便.功能丰富,但是踩得坑也很多,接下面一一说道. 目前Scintilla ...

  3. Java基础巩固——反射

    什么是反射 反射机制就是指程序运行时能够获取自身的信息.在Java中,只要给出类的名字,就可以通过反射机制来获取类的信息 哪里用的到反射机制 在jdbc中就是使用的反射来实例化对象,比如:Class. ...

  4. MariaDB 使用正则匹配查询(7)

    MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,MySQL由于现在闭源了,而能轻松成为MySQ ...

  5. Redis---事务和Wtach

    1. 概述 Redis通过 MULTI, EXEC / WATCH 等命令来实现事务. 事务提供一种将多个命令请求打包, 然后一次性.按顺序的执行多个命令的机制. 并且在事务执行期间, 服务器不会中断 ...

  6. Windows下Mongodb安装及配置

    安装文件:MongoDB-win32-x86_64-2008plus-ssl-3.2.6-signed.msi 电脑配置:win7 64位 mongodb的安装很简单,设置好安装路径后,一直Next直 ...

  7. postgresql-int,bigint,numeric效率测试

    在postgresql9.5的时候做过一个测试就是sum()的效率最终的测试结果是sum(int)>sum(numeric)>sum(bigint)当时比较诧异为啥sum(bigint)效 ...

  8. Vuex 拾遗

    引入Vuex的目的:为众多的Vue组件提供一个全局管理共享组件状态的控制中心,当一个共享状态改变时,能使调用该共享状态的组件得到更新.并且使用Vuex的API,每个共享状态的改变都能被追踪. 组件如何 ...

  9. linux 下NFS远程目录挂载

    NFS 是Network File System的缩写,中文意思是网络文件系统.它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录.NFS客户端(一般为应用服务器,例如web ...

  10. DDD漫想

    领域专用语言 领域驱动设计(Domain Driver Design)开发中,最令我震撼的是领域专用语言(Domain specific language),领域专用语言专注于描述当前领域内的业务细节 ...