同步/异步, 阻塞/非阻塞的理解

󰃭 2016-04-13

导读

对于同步、异步、阻塞、非阻塞这几个概念一直很模糊, 网上的很多人的理解也是模模糊糊, 看了半天, 并没啥太大的帮助, 在这里, 我也写一下我的理解, 尽量让自己和各位都能有更好更直观的理解

场景

这里所说的同步/异步, 阻塞/非阻塞都是针对网络io编程的

如何理解

对于同步/异步, 阻塞/非阻塞, 我一直都想不通前后者有多少区别, 同步不就阻塞了吗, 异步不是非阻塞的吗, 那为什么又出来两个概念呢。

其实, 我们要理解这几个概念, 首先要明白以下几个问题:

  1. 是谁阻塞/非阻塞的
  2. 为何阻塞/非阻塞
  3. 如何阻塞/非阻塞
  4. 是谁同步/异步操作的
  5. 为何同步/异步操作
  6. 如何同步/异步操作的

前三个是针对阻塞/非阻塞的认知, 后三个是针对同步/异步的认知, 如果能理解上面几个问题, 那么同步/异步,阻塞/非阻塞的问题就该很明了了

是谁阻塞/非阻塞

这就引入了网络编程中网络数据发送到服务器和服务器返回数据的过程了, 注意, 这里的过程仅仅是网络数据包被服务进程读取服务进程将数据通过连接返回给客户端的过程, 服务进程处理数据的过程并不属于这里的阻塞/非阻塞, 同步/异步的讨论范畴, 这是很多入门的人学习的误区, 看了半天不知道是针对什么做的阻塞/非阻塞, 同步/异步的。 尤其是很多人做了阻塞/非阻塞, 同步/异步的各种比喻(买书, 烧水, 打电话等等), 却没有人针对这个做说明, 结果看了比喻也不能特别明白啥。

继续, 针对以上两个过程, 分别对应服务器进程从socket对数据的读和写, 详细说明如下

网络io 是通过客户端和服务端的socket连接实现的, 每个client和server 的连接都会有一个连接通道(socket),服务进程要从socket中读取数据和写入数据, 然而其并不是直接读取数据的, 二者之间有个读的buffer和写的buffer, 这个buffer在内核中, 服务器继承实际上是实际从内核直接读取/写入数据,

读:
            读buffer                      copy
internet =================》服务器内核===============》 服务器进程
          
写:
            写buffer                      copy
internet 《================ 服务器内核《=============== 服务器进程

更精细的说, 阻塞/非阻塞, 阻塞/非阻塞是针对网络(internet)到服务器内核这个阶段之间的读写过程

为何阻塞/非阻塞

默认的I/O都是阻塞的, 这里的问题应该是为何要非阻塞: 提高并发能力,进程阻塞会导致服务进程的并发能力薄弱,同一时刻大部分的用户看到的页面和操作都很卡,这是最直接的体现,

如何阻塞/非阻塞

阻塞的时候, 服务器内核要等待buffer可读或可写(这里统一叫就绪)才能继续对新的数据包进行传输, 即老的socket连接要完成读/写, 新的socket才能再去读/写, 这样, 同一时刻, 服务器就只能处理一个socket连接, 这就很影响体验了

而非阻塞的时候, 在老的socket连接中, 内核在收到对buffer的读写请求时,不管怎么读写, 都能直接让新的socket连接进来并进行读写, 这样, 就出现了服务进程同时处理多个socket的局面。这样就实现了并发。

那么, 每个连接对应的服务进程怎么确定自己从内核读取了自己连接的数据? 系统内核会维护一个列表, 这个列表里存储了每个client和server的连接描述符, 这样, 服务进程就能通过各个连接的描述符得到属于自己的连接的数据并返回。

是谁同步/异步

这样说其实不好, 应该说同步/异步是针对什么

同步/异步针对的是服务器进程与内核之间的交互, 让服务器进程触发对socket连接的I/O,并在某个时机处理I/O的结果。

同步: 服务进程触发(要读/写数据)socket I/O后,等待I/O结果(即从buffer里完整读取网络数据包到内核或从内核写入数据到buffer), 然后才处理I/O的结果

异步: 服务进程触发socket I/O后, 不等待I/O结果, 直接返回某个值,服务进程继执行(没有数据怎么处理?这会在怎么同步/异步 中说明)

为何同步/异步

服务器的对socket的连接默认都是同步的, 所以这里的问题应该是为何要异步, 同步因为要等待I/O(无论是阻塞还是非阻塞的I/O)的结果(这个结果是internet和内核的I/O结果, 即便是非阻塞的I/O),即服务进程要等待内核数据的就绪, 所以也会将进程挂起, 导致并发能力降低, 而异步则并不等待结果

如何同步/异步

如果是同步的话, 服务进程等待I/O结果, 直接处理并返回,

那么如果是异步的呢? 进程在要求与内核通信异步时, 要有一个回调机制(比如通过回调函数), 异步的进程在稍后内核就绪(I/O有结果)了再通过这个回调机制返回结果. 在上面我们已知内核维护了一个连接列表, 通过这个连接列表就能给对应的连接返回结果了, 但却不会导致服务进程处理的阻塞

总结

按我的理解, 阻塞/非阻塞机制解决了网络I/O的通信阻塞为题, 让服务器(注意, 不是服务进程,是服务器)能够并发处理连接, 而同步/异步机制让服务进程(注意, 是进程)不被阻塞, 让进程能够并发的处理数据。二者的结合才让我们的服务的并发处理能力得到大幅度的提高。I/O多路复用就是这两种机制最好的提现, 希望以后有机会再写写这个也不错

以上是我对同步/异步和阻塞/非阻塞的理解, 如有错误, 请及时指出, 谢谢

PS: 请各位认真参考这个zhihu的回答(见参考链接)的图片加说明, 结合我的内容, 会更好理解

参考: https://www.zhihu.com/question/19732473/answer/26101328?utm_campaign=webshare&utm_source=weibo&utm_medium=zhihu