V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pythonee
V2EX  ›  编程

搞不清楚同步,阻塞,异步,非阻塞这些概念

  •  
  •   pythonee · 2013-04-06 09:51:35 +08:00 · 9553 次点击
    这是一个创建于 4282 天前的主题,其中的信息可能已经有所发展或是发生改变。
    同步,阻塞,异步,非阻塞,这几个概念实在是容易把人搞乱,查了很多资料,发现线程中的阻塞/非阻塞和IO中的概念还有点不一样,这又让人更加混乱了。

    现在,我只想搞清楚这几个概念在IO中的含义与实例,在理解这些概念的时候,没有实例,实在是有点难弄清楚。另外,java中的nio到底是异步非阻塞还是同步非阻塞啊,我见到了两种说法。我想,弄清楚上面那些概念后,这个答案也自然出来了。

    另外,有没有同步非阻塞或异步阻塞模型的实际用例啊
    30 条回复    1970-01-01 08:00:00 +08:00
    gamexg
        1
    gamexg  
       2013-04-06 11:25:58 +08:00   ❤️ 1
    阻塞就是指这个操作在完成前被调用函数不会返回,会把当前线程一直阻塞在那里。
    非阻塞就是不管进行的操作的完成情况,被调用函数立刻返回,当前线程继续执行下一步。
    同步一般可以理解为实际的操作是在当前线程执行的,异步一般指被调用函数内部建立了(也可能使用已存在的)一个新的线程来执行实际的操作,而不是当前线程。
    denger
        2
    denger  
       2013-04-07 12:38:26 +08:00
    异步/同步:比如 Email 的场景就是异步的,当你发送邮件后,成功与否你无法马上得到响应(比如出现 to address 不存在时),但它此时并没有阻塞(异步非阻塞),所以异步的本质是,当消息发送到对方没有立即得到期待的回应。 反而 HTTP 就是就是同步的,发送请求,并等待对方(server)马上得到回应,当然此时该 thread 处于阻塞的状态(同步阻塞)。

    阻塞/非阻塞:阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞(如线程是否被挂起),如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

    再说说以上两者的关系,异步IO的通常都是非阻塞IO的,但是非阻塞IO并不一定就是异步IO,所以不存在 "异步阻塞" 的说法。另外,阻塞IO肯定也是同步IO,反之,则不一定。

    关于Java,准确的说,同步阻塞是 BIO;同步非阻塞是 NIO;异步非阻塞是 AIO。可能有些人会说 NIO 是阻塞的,其主要原因我想是因为 Epoll,因为 Java 在 Linux 下是基于它实现,而 Epoll 的IO处理仍然会存在 wait 的过程,所以 Epoll 是阻塞的。(参考: http://stackoverflow.com/questions/7209057 )。 关于 Java NIO 可以参考:http://tutorials.jenkov.com/java-nio/nio-vs-io.html
    BOYPT
        3
    BOYPT  
       2013-04-07 12:57:31 +08:00   ❤️ 3
    这些概念都是从现实当中抽象出来的啊,举例子:

    同步:必须先吃完饭,再洗碗。

    阻塞:你请了个专门负责洗碗的工人,他一天到碗就等着洗碗,你吃饭的时候,碗被你拿着,洗碗工人就被阻塞了,必须等待,等到你吃完饭这个过程完成同步后,才开始洗碗。

    异步:那样的工人太懒了,怎么可能只洗碗呢,你吃饭的时候他不能洗碗,但是能擦窗啊,所以虽然洗碗这个工作被阻塞了,但是工人还在做其他不需要等待你吃完饭的工作,这是异步。

    非阻塞:因为洗碗这个过程必须要先获得碗,所以洗碗是不能是非阻塞的,但是工人可以随时问:“楼主你吃完饭了嘛”,你必须随时回答,这样的过程是非阻塞的。

    可见,阻塞与否是跟“碗”有关的,同/异步是跟工人有关的,在不同的对象角度就有不同的状态表达。
    denger
        4
    denger  
       2013-04-07 13:07:08 +08:00   ❤️ 1
    再补充:
    POSIX IO模型分布主要有,blocking IO,Non-blocking IO, IO multiplexing, signal driven IO, asynchronous IO. 前四种都是同步IO,只有最后一种为异步IO;

    通常同步IO与异步IO的主要区别在于:是否会阻塞请求;那么为什么 Non-Blocking IO 是既然字面意思是 非阻塞IO 而却是同步的呢? 主要在于 Non-Blocking IO 并非完全的不阻塞(IO请求分为两个阶段),而是在数据准备阶段不阻塞,数据拷贝阶段阻塞(参考 Epoll 原理);

    注:以上为个人的一些理解!仅供参考。
    sillyousu
        5
    sillyousu  
       2013-04-07 13:19:47 +08:00   ❤️ 1
    Unix Network Programming(Unix 网络编程)的第一卷的第六章的第二节。
    luoqeng
        6
    luoqeng  
       2013-04-07 14:59:24 +08:00
    同步,異步,是目的。阻塞,非阻塞是實現方式。

    注:以上为个人的一些理解!仅供参考。
    pythonee
        7
    pythonee  
    OP
       2013-04-07 22:56:02 +08:00
    @denger
    @sillyousu

    确实,读了之后清楚了
    pythonee
        8
    pythonee  
    OP
       2013-04-07 23:05:04 +08:00
    @denger 另外,我建议,还是不要把NIO叫作同步非阻塞IO,直接就叫IO复用是最合理的,同步非阻塞不准确且容易让我们这些初学者误会
    pythonee
        9
    pythonee  
    OP
       2013-04-08 08:46:29 +08:00
    @denger
    @sillyousu
    @BOYPT

    我还想问问各位,非阻塞IO中,去询问系统数据准备好没好,是不是对开发者透明的?还是需要应用自己去询问,另外,询问过程中,应用有没有让出cpu
    BOYPT
        10
    BOYPT  
       2013-04-08 09:58:52 +08:00
    @pythonee 非阻塞IO一般是不需要询问的,比如linux中打开了async的fd,read操作不管有没有数据都会马上返回,没数据就返回空值,不存在让出cpu的情况。
    pythonee
        11
    pythonee  
    OP
       2013-04-08 10:15:16 +08:00
    @BOYPT

    不对吧,应用在内核还未准备好数据的时候,是要不断询问的,当然,内核也是果断返回错误的,就像你说的,不断询问“楼主你吃完了吗?” 这样的问题

    另外,java中把socket设置non-block就这句话

    SocketChannel.configureBlocking(false)

    我理解这句话就是通过非阻塞去读取,貌似应用就没有做什么了
    heroicYang
        12
    heroicYang  
       2013-04-08 22:18:54 +08:00 via iPhone
    @luoqeng 反了吧?阻塞、非阻塞是目的,同步、异步是实现。
    sillyousu
        13
    sillyousu  
       2013-04-08 22:21:25 +08:00
    >应用在内核还未准备好数据的时候,是要不断询问的

    用户进程去可以不断询问一个 non-blocking socket,但是一般不这么做,这样效率很低。

    假如只有一个 socket 需要处理,直接 用阻塞 socket 或者 用阻塞 socket + 多线程。好像怎么都很好办。

    个人觉得:非阻塞 I/O 一般在处理多个 socket + 很多数据的时候比较好用。
    非阻塞 I/O 一般和 I/O 复用 (例如 select) 联合在一起使用。

    还是 UNP ,第一卷的十六章,那里讲了非阻塞 I/O 的几种用法。

    我搬运一点点,用来说一下 非阻塞 I/O 对 阻塞 I/O 的优势:

    blocking socket 用 connect 发起连接,
    TCP 需要连接需要等peer发回ack,所以这是会阻塞的。
    如果用 non-blocking socket 那么虽然 TCP 连接尚未真正建立起来,但 connect 立即返回,
    用户进程需要建立多个连接的时候就会比较有用。

    再有是,通过 select 选择出来一个可以写的 socket ,
    往这个 socket 做写入操作,如果这是 blocking socket 而且缓冲区剩余空间不够的话就会阻塞。

    如果是 non-blocking socket 那么会有多少缓冲空间就写多少,而不会停下来等。
    BOYPT
        14
    BOYPT  
       2013-04-09 08:45:35 +08:00
    @pythonee 不断询问是一个直接方式,但是实际上效率都太低了;最传统的select模型里面就是,注册了一系列读写fd,然后阻塞在一个accept请求上(可设置超时),只要fd们可读/可写,就返回。传统的模式就是开一个线程专门做这个事情。

    SocketChannel.configureBlocking(false)

    就是把socket设置成非阻塞模式,实际上究竟是否读写成功,按照具体的返回值判断。
    fangzhzh
        15
    fangzhzh  
       2013-04-09 09:15:48 +08:00   ❤️ 1
    两篇文章: 各种io模型, 有图有真相. 撸主不要说是csdn的,或者cppblog看不上眼, 看一看,内容很不错

    再谈select, iocp, epoll,kqueue及各种I/O复用机制: http://blog.csdn.net/shallwake/article/details/5265287

    [翻译] 两种高性能I/O设计模式(Reactor/Proactor)的比较: http://www.cppblog.com/pansunyou/archive/2011/01/26/io_design_patterns.html
    pythonee
        16
    pythonee  
    OP
       2013-04-09 10:28:17 +08:00
    @sillyousu
    @BOYPT

    我现在都要考虑是否开一个新帖来讨论了,据两位的回答

    SocketChannel.configureBlocking(false)

    这句话都是立即返回,那么这里的"非阻塞"就和上面的IO模型中的非阻塞不是同一个概念了,那么为何可以做到 “有多少写多少”,那我觉得这种读写适合所有IO模型啊,为何仅仅在IO复用模型中被广泛使用
    pythonee
        17
    pythonee  
    OP
       2013-04-09 10:33:02 +08:00
    @fangzhzh 怎么会看不上,上面可是很多大牛的文章呢
    pythonee
        18
    pythonee  
    OP
       2013-04-09 10:43:40 +08:00
    @BOYPT
    @sillyousu

    或者说,如果我改成

    SocketChannel.configureBlocking(true)

    那么,对IO复用模型有什么很大的影响吗?这样IO loop会慢下来?

    另外,回到非阻塞"有多少写多少"那个问题,我调用了write(buffer)后,如果内核没有足够的空间,或是buffer = fd.read(),这时应用空间没有足够大的buffer,那么按非阻塞的情况,那么是有多少写多少,有多少读多少,那么剩下的咋弄呢,下次select中了再读再写?
    sillyousu
        19
    sillyousu  
       2013-04-09 12:32:42 +08:00
    @pythonee
    >那我觉得这种读写适合所有IO模型啊,为何仅仅在IO复用模型中被广泛使用

    我想大概是因为非阻塞 I/O 一般用来处理大量数据和*很多* socket 的时候比较有效。
    而大量 socket 决定了要使用 I/O 复用。

    >SocketChannel.configureBlocking(true)
    >那么,对IO复用模型有什么很大的影响吗?这样IO loop会慢下来?
    这要看情景。UNP 上面的一个 benchmark 显示:使用非阻塞I/O 比使用阻塞I/O 快了一倍左右。

    >另外,回到非阻塞"有多少写多少"那个问题,我调用了write(buffer)后,如果内核没有足够的空
    >间,或是buffer = fd.read(),这时应用空间没有足够大的buffer,那么按非阻塞的情况,那么
    >是有多少写多少,有多少读多少,那么剩下的咋弄呢,下次select中了再读再写?

    是的。要自己维护一个缓冲区,剩下的下次在写。
    pythonee
        20
    pythonee  
    OP
       2013-04-10 20:10:39 +08:00
    @sillyousu

    SocketChannel.configureBlocking(false)

    这个非阻塞socket可以说是那个UNP中的非阻塞IO模型吗?总感觉你们说的是,但是需要确认一下
    sillyousu
        21
    sillyousu  
       2013-04-10 21:12:54 +08:00
    @pythonee

    我想应该是的。不过我不会 java 囧
    pythonee
        22
    pythonee  
    OP
       2013-04-10 21:55:29 +08:00
    @sillyousu

    呵呵,那我只好去跟下代码了,这个是个大坑我觉得
    G_will
        23
    G_will  
       2013-04-30 22:56:19 +08:00
    看了回帖觉得有必要插一腿:

    1. 举例子说这个问题很不好,越说越混乱,因为:a. 比喻只能给我们描述本体和喻体大致的相似,而正是那些细微的差别才是理解这个问题的关键 b. 所有现实中的例子都和计算机世界中有一个本质的差别就是现实中复制物体成本很大,计算机中复制就复制了。 回题,我们说的是计算机的描述 IO 的模型,那我们就以此说好了。

    2. 阻塞对应着非阻塞,但同步不对应着异步,异步的更多的意思是会有异步的情况发生但是也有可能是同步的。

    3. 阻塞非阻塞、异步同步,这两组概念并非正交,所以这两组词组合出的四种 IO 类型也不是完全的没有交集。

    4. 我们在说 IO 模型,指的是一种 IO 情景表现出的情形,我们可以说这个代码是属于某某 IO 模型,但不能说某某 IO 模型一定就是怎么怎么实现的。所以,当我们讨论不同范畴的代码逻辑时,IO 模型是有可能不同的,也许整体程序上是阻塞的,但是其中一个接口请求是非阻塞的。所以,很多讨论 select 和 epoll 是什么模型的时候就会陷入混乱,因为大家说的范畴不一样。
    G_will
        24
    G_will  
       2013-04-30 23:12:51 +08:00
    另外补一个同步非阻塞的实际用例, agentzh 老师领导的 OpenResty 项目中 ngx_lua 通常就是同步非阻塞的 IO 模型,当有 IO 任务时,主动将 IO 切分为更小的逻辑,并且 yield 控制权,所以当 IO 发生时,整个程序的执行顺序与编写预期顺序是一致的,同步,因为只进行一点 IO 就 yield 出进行处理,所以没有没有阻塞整个程序,是非阻塞。
    zava
        25
    zava  
       2013-05-02 14:09:24 +08:00
    @pythonee 可以参考一下我之前的博客, 关于IO的同步,异步,阻塞,非阻塞:http://www.zavakid.com/2011/07/30/unix-io-model/
    luoqeng
        26
    luoqeng  
       2013-05-03 12:30:05 +08:00
    同步和异步是指一个过程而言,同步:过程是按照固定次序执行的,前后次需是有明确逻辑语义的,不能颠倒。而异步,则对次序要求不严格。也就是说作为过程执行主体(如应用程序)在执行过程中是否等待 服务方的返回,等待则是同步,否则是异步。只有等待返回后才继续进行后续步骤。

    阻塞非阻塞:是指某执行函数、过程是否立刻返回。也就是程序调用某个函数是否立刻返回。在程序中,程序是由无数个函数、小过程体组成的,往往这些一般是系统预定义的。

    同步异步是站在程序的视角看的,同步是有次序不可随意划分颠倒的,异步次序不是固定的。
    阻塞非阻塞是指组成程序的各个单位、元素而言,粒度不同。这些单位组成了一个程序体。比如系统函数,当然也有自定义函数

    http://blog.chinaunix.net/uid-52437-id-2108895.html
    miaoever
        27
    miaoever  
       2013-05-03 12:37:48 +08:00
    毛遂自荐一篇自己的拙作,希望能有帮助
    http://miaoo.in/talk-about-unix-io-model.html
    barb
        28
    barb  
       2013-05-03 13:46:10 +08:00
    I/O 同异步方面的认识,的确是大师级程序员要搞明白的事
    est
        29
    est  
       2013-05-03 14:22:40 +08:00
    就拿去去银行办理业务,排队来说:

    blocking:人少直接办理业务;如果人多,就排队等待,再办理业务。
    non-block:人多需要排队 -> 办理业务失败 -> 直接放弃;人少 -> 运气好就直接办理下来了。

    sync:取号之后就呆呆等着一直轮到我开始办理业务。一次只做一件事。按顺序做事。
    async:取号之后,我去做点其他事情,轮到我了再办理业务。先后顺序不用管了。

    其中async的信号(或者说回调)也有两种模式,一种是轮到我了,反复喊号,一直到催促成功。另一种是一次喊号失败了就不管了。爱来不来。

    顺便介绍一下Unix下几种模式吧:

    复用(multiplexing):手里有多个业务需要办理。一次性观察多个窗口排队情况,哪个人少去哪个。

    SIGIO:把所有窗口流水号处理情况搜集起来,筛选出自己需要的窗口是否有空。有空就去办,否则继续看流水
    JackyHua
        30
    JackyHua  
       2013-05-04 15:23:47 +08:00
    不错学习了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1197 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:17 · PVG 02:17 · LAX 10:17 · JFK 13:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.