zoukankan      html  css  js  c++  java
  • 通俗系列之同步、异步、阻塞和非阻塞

    前言

    在日常的开发中,经常出现同步、异步、阻塞和非阻塞等概念。有些人搞不清楚什么代码是同步,什么代码是异步。有些人说我用异步了啊,为什么效率还是没提高呢?也许你是用异步了,但是可能是异步阻塞了。有些人一听说异步好,就不管三七二十一,所有方法全部改成异步,然后就会产生新的问题。归根结底还是对同步、异步、阻塞和非阻塞的概念不理解。

    那究竟什么是同步、异步、阻塞和非阻塞呢?我决定尝试用比较通俗的例子举例来解释一下,于是便有了这篇文章。

    同步

    概念

    什么是同步?同步就是一个任务的完成依赖于另外一个任务,只有被依赖的任务完成后,那么依赖的任务才能继续完成。

    这是一种非常可靠的任务序列,要么都成功,要么都失败。

    就和以前的瀑布式开发一样,你要做一个项目,一定要严格按照需求、分析、设计、编码、测试的步骤顺序执行,如果其中有一个问题卡住了,那么后边就无法继续,如果其中有一个环节是搞错了,那么这个项目也就可能死掉了。

    同步又分同步阻塞和同步非阻塞,接下来将依次举例解释。

    同步阻塞

    同步阻塞就是调用方在等待另一个任务的结果返回来之前,什么事也没做,就是死等,直到返回,而且具体要等待的结果是用来干嘛的,如果不返回,是不知道的。

    举例:

    快到饭点了,老王很饿,来到了一家饭店,老王跟老板说来份蛋炒饭。

    然后老板就是开始做,而老王就一直站在出饭的窗口等着,期间什么也没干,就是等着,直到老板把饭做好。

    说明:

    • 数据调用方:老王 (主线程)

    • 数据持有方:饭店老板

    • 要获得的数据:蛋炒饭

    • 同步:老王等着老板把饭做好

    • 阻塞:老王在等着,期间什么也没做

    这种调用方式很常见,这是以前普遍的方式,流水线式调用。

    如果老王就这样等着,那么效率是很低的。

    同步非阻塞

    同步非阻塞就是,你在等一件事A的结果时,可以继续去干另外一件事B。但是你需要时不时的回来瞧一瞧A的结果是否返回了。

    程序上通常都是用轮询去获取A事件是否完成并返回结果。

    举例:

    快到饭点了,老王很饿,来到了一家饭店,老王跟老板说来份蛋炒饭。

    然后老板就是开始做,而老王呢,就跑到饭店的门头透透气,顺便刷刷微博,但是老王需要时不时的跑进去问问老板饭好了没有

    说明:

    • 数据调用方:老王 (主线程)

    • 数据持有方:饭店老板

    • 要获得的数据:蛋炒饭

    • 同步:老王等着老板把饭做好

    • 非阻塞:老王等老板把饭做好,但是并不是站着死等,而是出门透透气,刷刷微博

    这种调用方式,一般在同步操作中,中间需要处理一个比较耗时的操作时,通常是另外开辟一个线程去处理这个比较耗时的操作,但是后边需要自己去轮询另外一个线程的执行结果是否完成。

    就比如上边,饭店老板做饭比较耗时,老王并没有一直等着,而是出门刷手机,但是饭有没有做好他需要时不时的跑进去问问,需要切换自己的位置。

    异步

    概念

    异步就是调用方不需要一直等待被依赖的操作完成,可以继续干其他事情,当被依赖的操作完成后,会自动通知调用方。

    调用异步操作时,被依赖的操作会直接返回一个任务或承诺给调用方,然后调用方会继续执行后边的操作。等被依赖的操作完成后,会通知调用方

    日常生活中就很常见,比如:

    领导给你安排一个任务,可能会说:小明你把xx项目的方案写一下,写完之后xx项目的相关人员一起去会议室开个会。小明肯定不能立即把方案写完,此时就会回答领导,“好的领导”。然后领导继续干自己的事,小明就去写方案,写完之后小明去通知领导,然后领导拿到方案后通知相关人员一起开会。

    异步阻塞

    异步阻塞就是虽然被依赖项完成时会通知调用方,但是调用方并没有继续去干其他事,而是依旧继续在那等着。

    举例:

    快到饭点了,老王很饿,来到了一家饭店,老王跟老板说来份蛋炒饭。

    老板在点菜机上下了单,并给了老王一个号,告诉老王等饭做好后,会叫号通知的。

    虽然会通知,但是老王很饿很着急,依旧站在出饭窗口等着。

    说明:

    • 数据调用方:老王 (主线程)

    • 数据持有方:饭店老板

    • 要获得的数据:蛋炒饭

    • 异步:饭做好了会叫号通知的。

    • 阻塞:虽然会通知,但是老王依旧站那等着饭做好,期间什么也没做。

    目前,现实社会中大部分场景都有了叫号功能,不需要一直排队等着。但是也免不了有个别人比较着急,即便是会叫号也一直站旁边等着。

    异步非阻塞

    异步非阻塞就是被依赖项完成时会通知调用方,调用方无需等待被依赖项,可以继续干其他跟依赖项结果无关的事。

    举例:

    快到饭点了,老王很饿,来到了一家饭店,老王跟老板说来份蛋炒饭。

    老板在点菜机上下了单,并给了老王一个单子,单子上是5号,告诉老王饭做好后,会叫这个号通知的。

    然后,老王拿着号找了个位置,刷起了新闻,期间还出门打了个电话,处理了一些工作上的事。突然老王听到叫号机通知5号的饭做好了,然后老王去窗口拿自己的饭。

    说明:

    • 数据调用方:老王 (主线程)

    • 数据持有方:饭店老板

    • 要获得的数据:蛋炒饭

    • 异步:饭做好了会叫号通知的。

    • 非阻塞:因为饭做好后会叫号,所以老王并没有一直站那等着,期间找了个位置刷起了新闻,还出去接了个电话

    这种效率是最高的,你做你的饭,我可以继续干不依赖于饭的事。你做好饭了叫我就行。

    异步在目前的前后端开发中都挺常见的。

    前端中通常会返回一个Promise(承诺)对象,.NET后端通常返回一个Task(任务),两个其实是一个意思。接下来以前端来举例例如:

    //AService.js 有两个方法,一个是获取数据getData(),一个是做一些耗时操作doSomething()
    async getData(){
        return await GetMyData()//该方法需要查数据比较耗时
    }
    //该方法就写一些日志
    async doSomething(){
        await writeLog()
    }
    
    //B.vue页面
    inint (){
        // 1 异步执行某个操作
        doSomething() 
        // 2 异步获取数据,然后对数据做xxx处理。代码的阅读和现实的语义是一样的。
        getData().then(res => {
    		//可以针对res做相应的处理
        })
        //3
        //... 不依赖于1、2的同步操作。
    }
    
    //上边doSomething()和getData().then()都会立即返回一个Promise(承诺),承诺会完成.
    

    上边1、2都是瞬间完成的,返回了一个Promise(承诺)。既然叫承诺,那就很好理解了,我承诺会完成,但不是立即完成。

    就比如开会的时候领导说,接下来安排一下任务:

    1. 张三,你把A项目的方案写一下,写完跟我讲一下,我们一起开会讨论一下,张三说”好的“。此时张三只是给领导了一个承诺,并没有立即完成。(异步
    2. 李四,你等会把B项目部署一下,看看运行效果。李四说”好的“。此时李四也是一个承诺,并没有完成B项目部署。(异步
    3. 然后领导将会议记录和任务安排发了给大家。(同步)

    上边的3并不依赖于1、2,领导不需要非要等张三把A项目方案写好,也不需要等李四把B项目部署好,再将会议记录和任务安排发给大家。


    如果您觉得这篇文章有帮助到你,欢迎推荐。如果您觉得哪里不对,也欢迎大家拍砖讨论。

  • 相关阅读:
    并发编程学习笔记(二十九、Unsafe)
    并发编程学习笔记(二十八、ConcurrentHashMap,Java8 ConcurrentHashMap)
    并发编程学习笔记(二十七、ConcurrentHashMap,Java7 ConcurrentHashMap)
    并发编程学习笔记(二十六、ConcurrentHashMap,Java8 HashMap简述)
    位运算符
    并发编程学习笔记(二十五、ConcurrentHashMap,Java7 HashMap简述)
    并发编程学习笔记(二十四、AQS总结)
    并发编程学习笔记(二十三、CopyOnWriteArrayList源码分析)
    我二十多岁了,至今依然一事无成
    从零开始手写 mybatis(一)MVP 版本
  • 原文地址:https://www.cnblogs.com/imlxp/p/14320293.html
Copyright © 2011-2022 走看看