共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。
在这篇文章中咱们来讨论一下到底什么是同步,什么是异步,以及在编程中这两个概念到底意味着什么,这些是进一步把握高性能、高并发技术的根底,因而十分要害。
置信很多同学遇到同步异步这两个词的时候大脑霎时就像红绿灯失灵的十字路口一样陷入一片懵逼的状态:
是的,这两个看上去很像实际上也很像的词汇给博主造成过很大的困扰,这两个词背地所代表的含意到底是什么呢?
咱们先从工作场景讲起。
苦逼程序员
假如当初老板调配给了你一个很紧急并且很重要的工作,让你上班前必须写完(万恶的资本主义)。为了督促进度,老板搬了个椅子坐在一边盯着你写代码。
你心里必定曾经骂上了“WTF,你有这么闲吗?盯着老子,你就不能去干点其余事件吗?”
老板好像接管到了你的脑电波一样:“我就在这等着,你写完前我哪也不去,厕所也不去”
这个例子中老板交给你工作后就始终 期待 什么都不做 直到你写完,这个场景就是所谓的同步。
第二天,老板又交给了你一项工作。
不过这次就没那么焦急啦,这次老板轻描淡写“小伙子能够啊,不错不错,你再致力干一年,明年我就财务自在了,明天的这个工作不焦急,你写完通知我一声就行”。
这次老板没有盯着你写代码而是转身刷视频去了,你写完后简略的和老板报告了一声“我写完了”。
这个例子老板交代完工作就去忙其它事件,你实现工作后简略的通知老板工作实现,这就是所谓的异步。
值得注意的是,在异步这种场景下重点是在你写代码的同时老板在本人刷剧,这两件事在同时进行 , 因而这就是为什么一般来说异步比同步高效的实质所在,不论同步异步利用在什么场景下。
因而,咱们能够看到同步这个词往往和工作的“依赖”、“关联”、“期待”等关键词相干,而异步往往和工作的“不依赖”,“无关联”,“无需期待”,“同时产生”等关键词相干。
By the way,如果遇到一个在身后盯着你写代码的老板,三十六计走为上策。
打电话与发邮件
作为一名苦逼的程序员是不能只顾埋头搬砖的,平时工作中的沟通罢黜不了,其中一种高效的沟通形式是吵架。。。啊不,是电话。
通常打电话时都是一个人在说另一个人听,一个人在说的时候另一个人 期待,等另一个人说完后再接着说,因而在这个场景中你能够看到,“依赖”、“关联”、“期待”这些关键词呈现了,因而打电话这种沟通形式就是所谓的同步。
另一种码农罕用的沟通形式是邮件。
邮件是另一种必不可少沟通形式,因为没有人傻等着你写邮件什么都不做,因而你能够慢慢悠悠的写,当你在写邮件时收件人能够去做一些像摸摸鱼啊、上个厕所、和同时埋怨一下为什么十一假期不放两周之类有意义的事件。
同时当你写完邮件收回去后也不须要水灵灵的等着对方什么都不做,你也能够做一些像摸鱼之类这样有意义的事件。
在这里,你写邮件他人摸鱼,这两件事又在同时进行,收件人和发件人都不须要互相期待,发件人写完邮件的时候简略的点个发送就能够了,收件人收到后就能够浏览啦,收件人和发件人不须要相互依赖、不须要互相期待。
你看,在这个场景下“不依赖”,“无关联”,“无需期待”这些关键词就呈现了,因而邮件这种沟通形式就是异步的。
同步调用
当初终于回到编程的主题啦。
既然当初咱们曾经了解了同步与异步在各种场景下的意义(I hope so),那么对于程序员来说该怎么了解同步与异步呢?
咱们先说同步调用,这是程序员最相熟的场景。
个别的函数调用都是同步的,就像这样:
funcA() {
// 期待函数 funcB 执行实现
funcB();
// 持续接下来的流程
}
funcA 调用 funcB,那么在 funcB 执行完前,funcA 中的后续代码都不会被执行,也就是说 funcA 必须 期待funcB 执行实现,就像这样:
从上图中咱们能够看到,在 funcB 运行期间 funcA 什么都做不了,这就是典型的同步。
留神,一般来说,像这种同步调用,funcA 和 funcB 是运行在同一个线程中的,这是最为常见的状况。
但值得注意的是,即便运行在两个不能线程中的函数也能够进行同步调用,像咱们进行 IO 操作时实际上底层是通过零碎调用(对于零碎调用请参考《程序员应如何了解零碎调用》)的形式向操作系统发出请求的,比方磁盘文件读取:
read(file, buf);
这就是咱们在《读取文件时,程序经验了什么》中形容的阻塞式 I /O,在 read 函数返回前程序是无奈持续向前推动的
read(file, buf);
// 程序暂停运行,// 期待文件读取实现后持续运行
如图所示:
只有当 read 函数返回后程序才能够被继续执行。
当然,这也是同步调用,然而和下面的同步调用不同的是,函数和被调函数运行在不同的线程中。
因而咱们能够得出结论,同步调用和函数与被调函数是否运行在同一个线程是没有关系的。
在这里咱们还要再次强调,同步形式下函数和被调函数无奈同时进行。
同步编程对程序员来说是最天然最容易了解的。
但容易了解的代价就是在一些场景下,留神,是在某些场景不是所有场景哦,同步并不是高效的,因为工作没有方法 同时 进行。
接下来咱们看异步调用。
异步调用
有同步调用就有异步调用。
对于 重要的异步调用 ,关注公众号“ 码农的荒岛求生”并回复“async”你就晓得了。
同步 vs 异步
咱们以常见的 Web 服务来举例说明这一问题。
一般来说 Web Server 接管到用户申请后会有一些典型的解决逻辑,最常见的就是数据库查问(当然,你也能够把这里的数据库查问换成其它 I / O 操作,比方磁盘读取、网络通信等),在这里咱们假设解决一次用户申请须要通过步骤 A、B、C 而后读取数据库,数据库读取实现后须要通过步骤 D、E、F,就像这样:
# 解决一次用户申请须要通过的步骤:A;
B;
C;
数据库读取;
D;E;F;
其中步骤 A、B、C 和 D、E、F 不须要任何 I /O,也就是说这六个步骤不须要读取文件、网络通信等,波及到 I / O 操作的只有数据库查问这一步。
一般来说这样的 Web Server 有两个典型的线程:主线程和数据库解决线程,留神,这探讨的只是典型的场景,具体业务实际上可会有差异,但这并不影响咱们用两个线程来阐明问题。
首先咱们来看下最简略的实现形式,也就是同步。
这种形式最为天然也最为容易了解:
// 主线程
main_thread() {
A;
B;
C;
发送数据库查问申请;
D;
E;
F;
}
// 数据库线程
DataBase_thread() {while(1) {数据库读取;}
}
这就是最为典型的同步办法,主线程在收回数据库查问申请后就会被阻塞而暂停运行,直到数据库查问结束前面的 D、E、F 才能够持续运行,就像这样:
从图中咱们能够看到,主线程中会有“空隙”,这个空隙就是主线程的“休闲时光”,主线程在这段休闲时光中须要期待数据库查问实现能力持续后续解决流程。
在这里主线程就好比监工的老板,数据库线程就好比苦逼搬砖的程序员,在搬完砖前老板什么都不做只是紧紧的盯着你,等你搬完砖后才去忙其它事件。
显然,高效的程序员是不能容忍主线程 偷懒 的。
是时候祭出大杀器了,这是什么大杀器呢,对于这个问题的答案关注公众号“码农的荒岛求生”并回复“回调”二字你就晓得答案了。
总结
在这篇文章中咱们从各种场景剖析了同步与异步这两个概念,然而不论在什么场景下,同步往往意味着单方要互相期待、相互依赖,而异步意味着单方互相独立、各行其是。心愿本篇能对大家了解这两个重要的概念有所帮忙。