聊聊同步、异步和回调
有一天,你找到公司刚来的程序员小T,跟他说:我们要加个需求,你放下手里的事情优先支持,我会一直等你做完再离开。小T微笑着答应了,眼角却滑过一丝不易觉察的杀意。
本文来自微信公众号“给产品经理讲技术”(pm_teacher),欢迎大家关注。
有一天,你找到公司刚来的程序员小T,跟他说:“我们要加个需求,你放下手里的事情优先支持,我会一直等你做完再离开”。小T微笑着答应了,眼角却滑过一丝不易觉察的杀意。
切入正题。世界上的所有事情大致可以分为同步去做和异步去做两种。你打电话去订酒店,电话另一边的工作人员需要查下他们的管理系统才能告诉你有没有房间。这时候你有两种选择,一种是不挂电话一直等待,直到工作人员查到为止(可能几分钟也可能几个小时,取决于他们的办事效率),这就是同步的。另一种是工作人员问了你的联系方式就挂断了电话,等他们查到之后再通知你,这就是异步的,这时候你就可以干点其他事情,比如把机票也定了之类的。同步和异步的区别就在于,在下达了执行任务的命令后,是等到执行完成之后才能得到结果呢,还是马上就知道了结果(尽管是不确定的答案)。
计算机世界也是如此。我们写的代码是交给CPU去执行的,在这个过程中经常面临是让CPU同步执行还是异步执行的选择。比如我写了一个APP,它可以帮你下载网络上的一个文件。当你输入一个文件的网址,按下下载按钮的一瞬间,CPU就收到了一个下载文件的任务。我们先想象一下同步执行时什么情况。CPU立刻停掉了手头的事情,包括绘制界面、对用户的点击做出响应等等,倾尽全力去帮你下载文件。但是,这时候你会发现,你的屏幕再也没有响应了,整个系统就像死了一样(废话,CPU都被你的下载任务抢走了)。过几秒钟,如果是Android系统则会弹出一个下面图1的提示,用户非常感动,然后无情的卸载了这个APP(尼玛裤子都脱了,你让我看这个)。同样的情况异步执行要好很多。CPU马上告诉你任务已经被受理了,等下载完成我会通知你的。于是呢,屏幕照样刷新,用户点击都能做出处理,就好像没有下载过一样。然而CPU并没有闲着,它开启了一个线程,专门处理这个下载任务(还记得之前讲过的线程的概念吗?不用担心我们下面会详细讲)。过了几个小时下载完了,你会收到一个通知,告诉你任务执行的结果(图2)。
一般情况下计算机通过多线程来实现同步,你可以把线程看做是富土康生产iPhone的一条生产线。它给生产一台完整的iPhone提供了所有必须的资源:包括人力,原料,设计图纸等等。生产任务来的时候,如果是同步的,那一条生产线就够了,所有的小伙伴们蜂拥而上,不一会儿就搞定了。如果是异步的,那就必须新建一条生产线(好在CPU创建线程的成本并不高),分一部分资源到新的生产线上,这样可以同时生产两台手机。那么生产线可以无限制增加吗?答案是不行的。一是异步会面临资源竞争的问题。比如说8条生产线都要组装电池,但是电池原料只有4份,那么必然会存在其他4条生产线等待的其情况,如果资源竞争比较频繁,甚至异步的执行效率要低于同步。二是异步会导致状态难以管理。比如车间主任想要统计一共生产了多少iPhone,就必须要询问完所有生产线才能得出结论,而且这个询问过程必须要停掉所有的生产线,同步来做。
讲到这里,回调的概念呼之欲出。上面异步任务的整个过程是首先你要把自己的信息给异步任务执行者,等执行完成的时候,执行者可以通过这些信息找到你,并给你一个通知。把自己信息给别人的过程叫做注册,别人找到你给你通知的过程就叫做回调。上面的例子,你把自己的联系方式给了酒店工作人员叫做注册,工作人员完成任务后联系你叫做回调。但是回调的概念其实非常广,这里可以抽象成先把要做的事情注册给别人,等条件满足的时候别人再回过头来调用你的模型。程序上响应一个按钮点击之后要做的事情也是用回调来做的。程序员先把用户点了按钮要做的事情先写好(比如要下载文件),注册给系统。等用户点击到按钮的时候,系统就会回调你下载文件的代码。
回到开篇,了解了同步、异步以及回调之后,你会这样跟小T说:”我们要加个需求,你抽时间支持下,等你做完了记得通知我。“小T欣然理接受,眼角闪过理解万岁的泪光,回头就把这事儿忘了。