一、摘论

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

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

并行,一般指并行计算,是说同一时刻有多条指令同时被执行,这些指令可能执行于同一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. Mac怎么安装并配置Homebrew?

    1.在打开的命令行工具中输入如下语句: 复制内容到剪贴板 ruby -e "$(curl --insecure -fsSL https://raw.githubusercontent.com ...

  2. JS学习笔记3_函数表达式

    1.函数表达式与函数声明的区别 函数声明有“提升”(hoisting)的特性,而函数表达式没有.也就是说,函数声明会在加载代码时被预先加载到context中,而函数表达式只有在执行表达式语句时才会被加 ...

  3. 一步一步学习Swift之(三):巧用AutoLayout布局

    一些初学者经常在使用autoLayout时,做得效果不太理想,经常会出现界面错乱的情况. 本文章用一个小实例说明autoLayout的使用 非常的简单,只要记住 规则就可以使界面适屏布局,适配各种ip ...

  4. solr 加载 停用/扩展词典

    startup.bat 停止词典的效果

  5. OC中双向链表的实现

    双向链表的概念 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都 ...

  6. Unity MVC 个人想法

    Unity MVC 个人想法 Model 想要实现效果 保存服务器数据 没有业务逻辑 接受发送消息 代码实现 缓存服务器发来的数据 注册消息接口 提供消息发送接口 View 想要实现效果 实现单元测试 ...

  7. rabbitmq系列五 之远程过程调用(RPC)

    1.远程过程调用(RPC) 在第二篇教程中我们介绍了如何使用工作队列(work queue)在多个工作者(woker)中间分发耗时的任务. 可是如果我们需要将一个函数运行在远程计算机上并且等待从那儿获 ...

  8. 11-02 Java Object类使用详解

     Object 作为超类 Object是类层次结构的根类,所有的类都直接或者间接的继承自Object类. Object类的构造方法有一个,并且是无参构造,这其实就是理解当时我们说过,子类构造方法默认访 ...

  9. 课程三(Structuring Machine Learning Projects),第一周(ML strategy(1)) —— 1.Machine learning Flight simulator:Bird recognition in the city of Peacetopia (case study)

    []To help you practice strategies for machine learning, the following exercise will present an in-de ...

  10. opencv实现canopy算法

    #include "stdafx.h" using namespace cv; int main(int argc, char** argv) { Mat img=imread(& ...