前几天一位朋友去面试,面试官问了他同步,异步,多线程之间是什么关系,异步比同步高效在哪?多线程比单线程高效在哪?由于回答的不好,让我帮他捋一下,其实回答这个问题不难,难就难在只对别人说理论,而没有现杀的例子。

一:异步

1. 到底解放了谁?

<1 style="box-sizing: border-box;"> 从基础的同步说起

要说解放了谁,一定得有几个参与者,举个例子:当你的主线程读取一个应用程序之外的资源时,它有可能是一个文件,又有可能是一个外部服务,当用同步方式读取外部服务时,首先主线程会从用户模式进入到内核模式,在内核模式中windows会将你的请求数据交给对应的网络驱动程序,继后会让这个线程进入休眠状态,当网络驱动程序和外部服务一阵痉挛之后,网络驱动程序会将获取到的结果交给当初休眠的线程,windows唤醒休眠线程继而执行后续的C#代码,画个简图理解一下,不一定全对。

这里就存在着一个非常大的问题,步骤4-步骤7之间,你的主线程一直都是休眠状态,比如在GUI编程中,有一个重要的原则就是解放你的UI线程(主线程),所以解决这个问题就迫在眉睫。

<2 style="box-sizing: border-box;"> 异步方式下的处理方案

说到这里,大家应该知道了异步方式就是为了解放主线程,又可以叫调用线程,没错,接下来看一下同样的场景在异步中如何处理的。

从图中可以看到,步骤三中将thread数据交给网络驱动程序之后,该thread就直接返回不管了,当后续网络驱动程序获取数据后,将数据丢给CLR线程池中的IO线程再由它触发你的回调函数。

<3 style="box-sizing: border-box;"> 总结

异步相比同步效率高就高在解放了调用线程,在驱动程序和远程服务RoundTrip期间,调用线程还可以执行其他工作,放在GUI上就是主线程可以继续响应用户的超敏操作。

由于没有空转的线程,CPU可以得到最满载的运转,更少的线程就有更少的线程栈空间,更少的GC回收时间和上下文切换。

2. 代码演示

还是那句话,光说可不行,你得上一点代码看看,有了上面的理论基础,这里我就模拟爬取下博客园首页的所有文章的用户头像。

<1 style="box-sizing: border-box;"> 同步代码

        public static void Main(string[] args){SingleThreadDownloadImages();Console.WriteLine("主线程继续执行其他的咯~~~");Console.Read();}public static void SingleThreadDownloadImages(){using (var client = new HttpClient()){//调用线程 空转等待。。。var content = client.GetStringAsync("http://cnblogs.com").Result;var html = new HtmlDocument();html.LoadHtml(content);var imgsrcList = html.DocumentNode.QuerySelectorAll("img.pfs").Select(m => m.Attributes["src"].Value).ToList();Console.WriteLine($"准备下载:{imgsrcList.Count}个...");for (int i = 0; i < imgsrcList.Count; i++){//调用线程 空转等待。。。var stream = client.GetStreamAsync(imgsrcList[i]).Result;Image.FromStream(stream).Save($@"C:\2\{i}.jpg");}}Console.WriteLine("SingleThreadDownloadImages 执行结束");}------ output ------准备下载:19个...
SingleThreadDownloadImages 执行结束
主线程继续执行其他的咯~~~

<2 style="box-sizing: border-box;"> 异步代码

        public static void Main(string[] args){AsyncDownloadImages();Console.WriteLine("主线程继续执行其他的咯~~~");Console.Read();}public static async void AsyncDownloadImages(){using (var client = new HttpClient()){var content = await client.GetStringAsync("http://cnblogs.com");var html = new HtmlDocument();html.LoadHtml(content);var imgsrcList = html.DocumentNode.QuerySelectorAll("img.pfs").Select(m => m.Attributes["src"].Value).ToList();Console.WriteLine($"准备下载:{imgsrcList.Count}个...");for (int i = 0; i < imgsrcList.Count; i++){var stream = await client.GetStreamAsync(imgsrcList[i]);Image.FromStream(stream).Save($@"C:\2\{i}.jpg");}Console.WriteLine("AsyncDownloadImages 执行结束");}}------ output ------主线程继续执行其他的咯~~~
准备下载:19个...
AsyncDownloadImages 执行结束

从结果可以看出,异步在获取图片期间,主线程还可以做其他事情,这就是异步最大的特点。

3. windbg 提取是否真为线程池io线程

其实在图2中我口口声声的说是线程池中的IO线程回调了你的函数,大家先要明白一个概念,线程池中有两种类别的线程,一个是工作线程,一个是IO线程,而工作线程常常就是我们通过代码进行操控,IO线程通常由底层CLR接管,常常用于处理外部资源的操作,如下ThreadPool的GetMaxThreads方法。

    public static void GetMaxThreads(out int workerThreads, out int completionPortThreads);

有了这个基础,再将 AsyncDownloadImages方法修改如下,抓取一下dump文件

                var content = await client.GetStringAsync("http://cnblogs.com");Console.WriteLine($"已获取到:{content.Length}个字符");Console.ReadLine();

~*e !clrstack 查看所有托管线程的调用堆栈

0:000>  ~*e    !clrstack
OS Thread Id: 0x62d8 (13)Child SP               IP Call Site
000000da9b1fd1e8 00007ff9fc7bb4f4 [GCFrame: 000000da9b1fd1e8]
000000da9b1fd308 00007ff9fc7bb4f4 [GCFrame: 000000da9b1fd308]
000000da9b1fd368 00007ff9fc7bb4f4 [HelperMethodFrame_1OBJ: 000000da9b1fd368] System.Threading.Monitor.Enter(System.Object)
000000da9b1fd460 00007ff9e42f8aff System.IO.TextReader+SyncTextReader.ReadLine()
000000da9b1fd4c0 00007ff9e40f0d98 System.Console.ReadLine()
000000da9b1fd4f0 00007ff985c81559 ConsoleApp2.Program+d__3.MoveNext() [C:\dream\Csharp\ConsoleApp1\ConsoleApp2\Program.cs @ 93]
000000da9b1fd690 00007ff9e388cef2 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000da9b1fd760 00007ff9e388cd75 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000da9b1fd790 00007ff9e38fbe2f System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run()
000000da9b1fd7e0 00007ff9e3901343 System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action, Boolean, System.Threading.Tasks.Task ByRef)
000000da9b1fd830 00007ff9e3865f40 System.Threading.Tasks.Task.FinishContinuations()
000000da9b1fd8c0 00007ff9e3865a88 System.Threading.Tasks.Task`1[[System.__Canon, mscorlib]].TrySetResult(System.__Canon)
000000da9b1fd900 00007ff9e3865a05 System.Threading.Tasks.TaskCompletionSource`1[[System.__Canon, mscorlib]].TrySetResult(System.__Canon)
000000da9b1fd940 00007ff9c88311a3 System.Net.Http.HttpClient+c__DisplayClass31_0`1[[System.__Canon, mscorlib]].b__1(System.Threading.Tasks.Task`1)
000000da9b1fd990 00007ff9e38f9d47 System.Threading.Tasks.Task.Execute()
000000da9b1fd9d0 00007ff9e388cef2 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000da9b1fdaa0 00007ff9e388cd75 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000da9b1fdad0 00007ff9e38fa001 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)
000000da9b1fdb80 00007ff9e38f96e1 System.Threading.Tasks.Task.ExecuteEntry(Boolean)

!threads 查看编号13的线程类型


0:013> !threads
ThreadCount:      8
UnstartedThread:  0
BackgroundThread: 5
PendingThread:    0
DeadThread:       2
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1 5754 000001e2be060f80    2a020 Preemptive  000001E2BFD19868:000001E2BFD19FD0 000001e2be053bb0 1     MTA6    2 65e0 000001e2be08bd00    2b220 Preemptive  0000000000000000:0000000000000000 000001e2be053bb0 0     MTA (Finalizer)9    3  25c 000001e2d8435ef0  102a220 Preemptive  0000000000000000:0000000000000000 000001e2be053bb0 0     MTA (Threadpool Worker)
XXXX    4    0 000001e2d845ea30  1039820 Preemptive  0000000000000000:0000000000000000 000001e2be053bb0 0     Ukn (Threadpool Worker)12    6 23fc 000001e2d8469ea0  202b220 Preemptive  000001E2BFD1E188:000001E2BFD1FFD0 000001e2be053bb0 1     MTA13    7 62d8 000001e2d8475e20  a029220 Preemptive  000001E2BFD9D588:000001E2BFD9F250 000001e2be053bb0 0     MTA (Threadpool Completion Port)
XXXX    8    0 000001e2d847a0b0  8039820 Preemptive  0000000000000000:0000000000000000 000001e2be053bb0 0     Ukn (Threadpool Completion Port)14    9  6e4 000001e2d847de70  8029220 Preemptive  000001E2BFD80D88:000001E2BFD81F10 000001e2be053bb0 0     MTA (Threadpool Completion Port)

其中的 13 7 62d8 000001e2d8475e20 a029220 Preemptive 000001E2BFD9D588:000001E2BFD9F250 000001e2be053bb0 0 MTA (Threadpool Completion Port) 可以明显的看到是 Threadpool Completion Port,没有骗你吧,????。

二:多线程

相比单线程,多线程用更多的CPU和更多的线程资源换取更快的计算时间,是一种经典的空间换时间策略,代码就不上了,相信多线程大家都快用烂了。

三:总结

1. 多线程比单线程高效的原因就是利用了CPU的多核计算把一个大的任务分而治之从而加速任务计算。

2. 异步比同步高效的原因是前者释放了调用线程,让调用线程可以做更多的事情而不至于被windows强制休眠浪费线程资源。

言简意赅吧~, 好了,本篇就说到这里,希望对你有帮助。


您的转发是我写作最大的动力

同步异步多线程这三者关系,你能给面试官一个满意的回答吗?相关推荐

  1. 2020年的Java程序员面试三件套:多线程+算法,看完吊打面试官

    第6章:"Read-Write Lock模式--大家一起读没问题,但读的时候不要写哦"将介绍Read-Write Lock模式,该模式会采用灵活的互斥处理.在该模式下,写数据的线程 ...

  2. 那些年搞不懂的多线程、同步异步及阻塞和非阻塞(二)---概念区分

    概念解释: 同步/异步, 它们是消息的通知机制 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回. 简单来说就是当前程序执行完才能执行后面的程序,程序执行时按照顺序执行, ...

  3. 【Java线程】互斥 同步 异步 并发 多线程的区别与联系

    1. 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行.其中两种并发关系分别是同步和互斥: 2. 互斥:进程间相互排斥的使用临界资源的 ...

  4. 过程 线 多线程 并发 同步异步

    过程 线 多线程 并发 同步异步 好多人的过程,线,多线程,并发,同步,异步概念混淆,这不是一个好大学讲学的缘故啊.在这里,我们感受到的概念,帮助学生感受审查困惑. 计划 用来描述个别功能程序中描述的 ...

  5. 进程线程、同步异步、阻塞非阻塞、并发并行、多线程

    一: 进程和线程 1: 进程(Process) 是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程.线 ...

  6. 同步异步单线程多线程初级理解

    对于我开始接触同步异步单线程多线程的概念的时候,都是分别理解同步和异步.单线程和多线程概念,当看到"使用同步方法保证线程安全"时愚昧的理解为那就是单线程咯:于是就陷入了困惑,同步等 ...

  7. C#.net同步异步SOCKET通讯和多线程总结(转)

    C#.net同步异步SOCKET通讯和多线程总结 来源:http://www.cnblogs.com/Silverlight_Team/archive/2009/03/13/1411136.html ...

  8. iOS 多线程的简单理解(1) 方式 :同步 异步

    最近遇到特别糟糕的面试,过程中提到多次对多线程的处理问题,并没有很好的给予答复和解决,所以在这里做个简单的备案: 期望能更加了解和熟练使用 多线程技术: 下面都是自己的总结,如果存在不对的,或者不足, ...

  9. 一则故事表达:并发,并行,同步,异步,线程,多线程

    一个小事件说明下并发,并行,同步,异步,线程,多线程 一个广交会举办向8间公司发起展览邀请, 参展公司有8间,场地有80万平方米的展示区域, 每个参展商有10万平方米可以用于展出售卖, 每个参展公司仅 ...

最新文章

  1. UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc7 in position 0: invalid continuation byte
  2. python发邮件11002_【python发送zabbix报警邮件,SSL版本】mailman.py
  3. SAP Spartacus develop branch 的服务器端渲染启动方式
  4. 【PyTorch】PixelShuffle
  5. 消费分期群体-在校大学生和职场白领
  6. my makefile 自动推导
  7. VIIRS SDR数据预处理(二)
  8. 网络安全要学python_请问入门网络安全要学什么?
  9. NumPy学习笔记之random.randn()函数
  10. Vulkan Samples 阅读 -- Basics(五): Input Attachments Sub Passes Offscreen Renderings
  11. java 走马灯程序_微信小程序实现简单跑马灯效果
  12. QT 屏幕旋转的两种方式
  13. android opengl版本太低,安卓模拟器opengl_安卓模拟器无法安装“系统opengl版本过低”的通用解决方法_安卓模拟器通用版_通用安卓模拟器...
  14. 语文七年级计算机作文,我从电脑游戏中学到了语文初一作文
  15. 在 Pages文稿中怎样给文本添加阴影、外框?
  16. 微信小程序手机号输入3和7位空格,删除时删除空格
  17. rebar3 的使用 1
  18. 查询每个部门中入职日期一致的员工信息
  19. CdlinuxU盘启动,小白教程,不要再看网上那些乱起八糟的了。
  20. 君子务本,本立而道生;敬事而信,直道而事人

热门文章

  1. IOS-网络(大文件下载)
  2. 通过邮箱远程控制电脑
  3. android 学习查阅笔记
  4. Teams数据统计 - 用户在线离线状态
  5. windows驱动程序编写_如何在Windows中回滚驱动程序
  6. plex 乱码_Plex Media Center现在支持播客
  7. 如何使计算机为您读取文档
  8. 亚马逊echo中国使用_如何使用亚马逊的主要照片备份所有照片
  9. 修改GIT的user.name和user.email
  10. log4net日志插件的使用