美文网首页基础知识
怎样理解阻塞非阻塞与同步异步的区别?

怎样理解阻塞非阻塞与同步异步的区别?

作者: yang2yang | 来源:发表于2016-10-03 23:20 被阅读4514次

    回答一

    转载自:银月游侠

    这几个概念,上面不少答案已经写得很清楚了。这里我结合自己的理解,简单地聊一下为什么这几个概念容易混淆。如果有错误之处,恳请批评指正。我认为同步、异步、阻塞、非阻塞,是分3个层次的:CPU层次;线程层次;程序员感知层次。

    这几个概念之所以容易混淆,是因为没有分清楚是在哪个层次进行讨论。

    CPU层次
    在CPU层次,或者说操作系统进行IO和任务调度的层次,现代操作系统通常使用异步非阻塞方式进行IO(有少部分IO可能会使用同步非阻塞轮询),即发出IO请求之后,并不等待IO操作完成,而是继续执行下面的指令(非阻塞),IO操作和CPU指令互不干扰(异步),最后通过中断的方式来通知IO操作完成结果。

    线程层次
    在线程层次,或者说操作系统调度单元的层次,操作系统为了减轻程序员的思考负担,将底层的异步非阻塞的IO方式进行封装,把相关系统调用(如read,write等)以同步的方式展现出来。然而,同步阻塞的IO会使线程挂起,同步非阻塞的IO会消耗CPU资源在轮询上。为了解决这一问题,就有3种思路:多线程(同步阻塞);
    IO多路复用(select,poll,epoll)(同步非阻塞,严格地来讲,是把阻塞点改变了位置);
    直接暴露出异步的IO接口,如kernel-aio和IOCP(异步非阻塞)。

    程序员感知层次
    在Linux中,上面提到的第2种思路用得比较广泛,也是比较理想的解决方案。然而,直接使用select之类的接口,依然比较复杂,所以各种库和框架百花齐放,都试图对IO多路复用进行封装。此时,库和框架提供的API又可以选择是以同步的方式还是异步的方式来展现。如python的asyncio库中,就通过协程,提供了同步阻塞式的API;如node.js中,就通过回调函数,提供了异步非阻塞式的API。总结因此,我们在讨论同步、异步、阻塞、非阻塞时,必须先明确是在哪个层次进行讨论。比如node.js,我们可以说她在程序员感知层次提供了异步非阻塞的API,也可以说在Linux下,她在线程层次以同步非阻塞的epoll来实现。

    回答二

    转载自:愚抄

    老张爱喝茶,废话不说,煮开水。
    出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
    1 老张把水壶放到火上,立等水开。(同步阻塞)
    老张觉得自己有点傻
    2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
    老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
    3 老张把响水壶放到火上,立等水开。(异步阻塞)
    老张觉得这样傻等意义不大
    4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
    老张觉得自己聪明了。

    所谓同步异步,只是对于水壶而言。
    普通水壶,同步;响水壶,异步。
    虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
    同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

    所谓阻塞非阻塞,仅仅对于老张而言。
    立等的老张,阻塞;看电视的老张,非阻塞。
    情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

    回答三

    转载自:大姚

    网络编程释疑之:同步,异步,阻塞,非阻塞
    一讲到网络编程的I/O模型,总会涉及到这几个概念。问了很多人,没几个能清晰地讲出他们之间的区别联系,甚至在网络上也有很多不同的观点,也不知是中国文字释义的博大精深,还是本来这几个概念就是绕人不倦。今天我也来给大家讲解一下我对这几个概念的理解。

    既然网络上众说纷纭,不如找个权威参考一下,这个权威就是《UNIX网络编程:卷一》**第六章——I/O复用。书中向我们提及了5种类UNIX下可用的I/O模型:

    • 阻塞式I/O;
    • 非阻塞式I/O;
    • I/O复用(select,poll,epoll...);
    • 信号驱动式I/O(SIGIO);
    • 异步I/O(POSIX的aio_系列函数);

    阻塞式I/O模型:默认情况下,所有套接字都是阻塞的。怎么理解?先理解这么个流程,一个输入操作通常包括两个不同阶段:
    (1)等待数据准备好;(2)从内核向进程复制数据。
    对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。 好,下面我们以阻塞套接字的recvfrom的的调用图来说明阻塞


    标红的这部分过程就是阻塞,直到阻塞结束recvfrom才能返回。

    非阻塞式I/O: 以下这句话很重要:进程把一个套接字设置成非阻塞是在通知内核,当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。看看非阻塞的套接字的recvfrom操作如何进行

    可以看出recvfrom总是立即返回。

    I/O多路复用:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。如图

    信号驱动式I/O:用的很少,就不做讲解了。直接上图

    异步I/O:这类函数的工作机制是告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到用户空间)完成后通知我们。如图:
    注意红线标记处说明在调用时就可以立马返回,等函数操作完成会通知我们。
    等等,大家一定要问了,同步这个概念你怎么没涉及啊?别急,您先看总结。 其实前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。相反,异步I/O模型在这两个阶段都要处理。
    再看POSIX对这两个术语的定义:
    同步I/O操作:导致请求进程阻塞,直到I/O操作完成;
    异步I/O操作:不导致请求进程阻塞。

    好,下面我用我的语言来总结一下阻塞,非阻塞,同步,异步
    阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待;
    同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。

    一些想法

    我觉得这个回答胜在对于同步,异步,阻塞,非阻塞的框架的搭建非常好,当然还有一些在各个层次的细节,可以参见其他回答,也是非常优秀的,比如说这个网络编程释疑之:同步,异步,阻塞,非阻塞

    还有其他优质的回答见:怎样理解阻塞非阻塞与同步异步的区别?

    相关文章

      网友评论

      • Spider_5f12:图片挂掉了,但是写的非常好~
      • yang2yang:这个问题下面的知乎答案写的都很好呀,有种拨开云雾的感觉

      本文标题:怎样理解阻塞非阻塞与同步异步的区别?

      本文链接:https://www.haomeiwen.com/subject/zagdyttx.html