当前位置:首页 > 技术学院 > 技术前线
[导读]同步和异步这两个概念,几乎是所有计算机领域里出现频率最高的术语之一。从操作系统的IO模型、后端服务的接口调用,到前端的网络请求、多线程任务调度,到处都能看到它们的身影。很多开发者背了无数次两者的定义,刷了一堆相关的面试题,但是一放到真实的业务场景里,就分不清眼前的逻辑到底是同步还是异步,甚至经常把同步异步和阻塞非阻塞混为一谈。同步和异步从来不是局限在某个技术领域的特殊概念,它是一套通用的交互逻辑范式,从日常生活的协作模式到计算机系统的分层交互,这套逻辑无处不在。彻底理清它的核心本质,你就能看透几乎所有分布式系统、高并发架构里的交互设计思路。

同步和异步这两个概念,几乎是所有计算机领域里出现频率最高的术语之一。从操作系统的IO模型、后端服务的接口调用,到前端的网络请求、多线程任务调度,到处都能看到它们的身影。很多开发者背了无数次两者的定义,刷了一堆相关的面试题,但是一放到真实的业务场景里,就分不清眼前的逻辑到底是同步还是异步,甚至经常把同步异步和阻塞非阻塞混为一谈。同步和异步从来不是局限在某个技术领域的特殊概念,它是一套通用的交互逻辑范式,从日常生活的协作模式到计算机系统的分层交互,这套逻辑无处不在。彻底理清它的核心本质,你就能看透几乎所有分布式系统、高并发架构里的交互设计思路。

一、核心本质:交互过程里的消息通知机制差异

同步和异步的核心区别,根本不是“调用之后会不会卡住等待”这么简单,两者最本质的分野,是任务发起方和任务执行方之间的消息通信机制完全不同。

同步交互模式下,任务发起方发出调用请求之后,必须全程等待,直到任务执行方把整个任务完全处理完成,带着最终结果回来,发起方才能继续执行后续的其他动作。整个过程里,发起方的执行流程被任务完全绑定,没有办法中途抽身去做别的事情,所有的执行步骤严格按照先后顺序串行推进,前一个任务100%完成,才能启动下一个任务。

异步交互模式下,任务发起方发出调用请求之后,不需要原地等待任务执行完成,请求发出去的瞬间就可以立刻返回,直接去执行后续的其他工作。任务执行方在后台慢慢处理整个任务,等任务100%执行完成之后,再通过主动通知、回调函数、状态变更等方式,把最终的执行结果送还给任务发起方。整个过程里,任务发起方的执行流程完全不会被阻塞,可以并行推进其他工作,不需要全程盯着任务的执行进度。

我们用最日常的生活场景就能一秒看懂两者的差异:你去线下餐厅点一份现做的牛排,如果采用同步模式,你点完餐之后就站在出餐口一动不动,什么别的事情都不做,一直站着等20分钟直到服务员把牛排递到你手里,整个等待过程你完全不能离开,这就是标准的同步交互。如果采用异步模式,你点完餐之后服务员给你一个取餐号,告诉你做好了会喊你,你不用在出餐口傻等,可以回到座位上刷手机、和朋友聊天,20分钟之后服务员喊你的号通知你取餐,你再起身去拿牛排,这就是最典型的异步交互。

很多人在这里会产生一个非常经典的认知误区:把同步异步和阻塞非阻塞直接画上等号。实际上这两组概念描述的是完全不同维度的东西:同步和异步描述的是任务发起方和执行方之间的消息通知机制,是整个交互流程的顶层设计范式;而阻塞和非阻塞描述的是任务发起方在等待结果的过程中,自身线程的状态是挂起休眠还是轮询检查。两者完全可以自由组合,异步交互完全可以搭配阻塞等待的实现,同步交互也可以用非阻塞轮询的方式来实现,根本不是绑定关系。

二、技术场景里的同步异步全景落地

这套交互逻辑在计算机的各个技术层级里,都有极其广泛的落地,不同层级的同步异步设计,共同构建起了整个系统的高并发能力。

在操作系统的IO模型层级,同步IO模式下,应用程序发起读文件的系统调用之后,线程会被直接挂起,一直等到磁盘把数据完整读到内存缓冲区里,系统调用才会返回,应用程序才能拿到数据继续执行后续逻辑。整个过程里应用程序什么别的事情都做不了,全程等待IO操作完成。而异步IO模式下,应用程序发起读文件的请求之后,系统调用立刻返回,线程可以继续去处理其他任务,操作系统在后台默默和磁盘硬件交互,把数据从磁盘读到指定的内存区域,整个IO操作全部完成之后,操作系统会通过信号或者回调队列的方式主动通知应用程序,应用程序直接拿到已经就绪的完整数据,不需要做任何额外的等待。

在后端分布式服务的交互场景里,同步调用是最常见的模式:服务A调用服务B的接口,发出请求之后,服务A的线程就卡在那里等待服务B返回结果,整个过程里这个线程什么别的请求都处理不了,直到拿到服务B的返回响应,线程才会继续执行后续逻辑。这种模式的好处是代码逻辑简单直观,异常处理容易,但是在高并发场景下很容易把服务的线程池打满,吞吐量遇到明显的天花板。而异步调用模式下,服务A发出请求之后,线程立刻放回线程池去处理其他新的用户请求,等服务B的响应通过网络回调回来的时候,再唤醒对应的处理线程继续执行后续逻辑。整个过程里不会有线程被长时间阻塞在等待响应的状态,系统的吞吐量可以提升数倍甚至数十倍,高并发场景下的优势极其明显。

在前端开发场景里,最典型的同步模式就是早期的同步AJAX请求,浏览器发起网络请求之后,整个浏览器的主线程被完全卡住,用户点击页面、滚动页面的所有操作全部没有响应,一直等到网络请求返回结果之后,页面才能恢复交互。这种模式的用户体验极其糟糕,现在几乎已经被完全淘汰。现在主流的异步AJAX请求模式,发起请求之后主线程完全不会卡住,用户可以继续正常操作页面,等网络请求在后台加载完成之后,通过回调函数更新页面里对应的内容,整个交互过程流畅不卡顿,这也是现在所有现代前端框架的标准交互模式。

三、异步模式的核心价值与实现复杂度权衡

异步交互模式最核心的价值,就是可以把原本串行阻塞的流程,转换成并行执行的多个任务,最大化利用系统的计算资源,把整个系统的吞吐量提升一个数量级。在IO密集型的场景里,磁盘、网络这类硬件的处理速度远远慢于CPU的运算速度,99%的时间里CPU都在原地等待硬件返回数据,资源被完全浪费。用异步模式把等待的时间利用起来,CPU就可以同时处理成百上千个任务,整个系统的并发承载能力得到质的飞跃。

但是异步模式从来不是银弹,它的实现复杂度和调试难度远远高于同步模式。同步模式的代码逻辑是完全线性的,前一行执行完再执行下一行,整个逻辑的执行顺序一目了然,出了问题顺着调用栈就能快速定位根因。而异步模式的代码执行流是碎片化的,任务发起和结果回调之间可能隔了成百上千个其他任务的执行,调试的时候根本没办法顺着单步调试的逻辑追踪整个流程,异常处理的复杂度也会大幅上升。很多新手写异步代码的时候,很容易出现回调地狱的问题,多层回调嵌套之后,代码的可读性和可维护性直接崩盘,后续几乎没人能接手维护。

所以在实际的工程选型里,绝对不能盲目追求异步,要根据业务场景的特征做权衡。如果业务逻辑非常简单,并发量很低,同步模式的代码足够清晰好维护,就完全没有必要强行改成异步模式,额外增加开发和调试的成本。只有当系统的并发量上来之后,同步模式的线程资源被大量阻塞,性能已经成为明显的瓶颈的时候,再逐步把核心路径的逻辑改成异步模式,用复杂度换吞吐量的提升,这才是最合理的工程决策。

四、现代异步编程范式的演进之路

为了解决原生异步回调的回调地狱问题,现代编程语言陆续推出了很多更优雅的异步编程范式。从最早期的回调函数模式,演进到Promise链式调用,再到现在几乎所有主流语言都支持的协程和async/await语法,开发者可以用几乎和同步代码一样的线性写法,来编写异步逻辑,既保留了异步模式的高性能优势,又拥有了同步代码的可读性和可维护性,彻底把异步编程的门槛降到了普通开发者可以轻松驾驭的程度。

同步和异步的逻辑贯穿了整个计算机技术体系的所有层级,理解它的核心本质,你就不会再被各种复杂的技术名词迷惑,能一眼看穿不同架构设计背后的交互逻辑本质。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭