[服务器端编码]同步、异步、阻塞、非阻塞

W_法 3月前 ⋅ 419 阅读

       在编码时,无论前端还是后端,会遇到同步(Synchronous)和异步(asynchronous)的概念。但是,由于同步和异步常常与“阻塞(blocking)”和“非阻塞(non-blocking)”交叉使用,常常造成混淆。如很多人认为“同步一定阻塞”、“异步就是非阻塞”、“阻塞一定同步”、“非阻塞一定异步”等等。其实这些判定都有使用上下文。首先,同步和异步既有可能是代码调用,也有可能是IO。其次,同步和异步的英文含义与其中文理解不同。

一、原则

       先稳定一种理论,然后再基于这个理论做一些思考。不然,很容易迷失,也就更难以理清其中的关系。

       https://github.com/calidion/calidion.github.io/issues/40

二、IO简介

        在正式介绍同步、异步等概念前,先对IO进行简单介绍。网上看到一篇不错的文章:https://blog.csdn.net/tjiyu/article/details/52959418,对其介绍的很清楚。

        IO(Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<-->内核空间、内核空间<-->设备空间(磁盘、网络等)。IO有内存IO(访问缓存)、网络IO磁盘IO三种,由于后两种IO耗时较长,通常意义的IO均指后两者。

        接下来,以Linux为例,介绍IO处理流程。Linux下用户进程无法直接操作I/O设备,必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。

        以一个输入操作来说,用户进程执行系统IO调用后,内核会先看检查缓冲区,如果,没有相关数据,会到设备中读取数据。由于设备IO操作速度较慢,在数据准备好前,需要等待。

三、阻塞和非阻塞与阻塞IO和非阻塞IO

      在介绍“同步”和“异步”前,先对比较容易区分的“阻塞(Blocking)”和“非阻塞(Non-Blocking)”进行分析。在区分阻塞和非阻塞时,还会遇到阻塞IO和非阻塞IO。

3.1 阻塞和非阻塞

        阻塞非阻塞是关于线程与进程的。其简单理解是:

     阻塞是指调用线程或者进程被操作系统挂起。更多线程/进程阻塞可以参考:https://blog.csdn.net/u012593344/article/details/51246437

        非阻塞是指调用线程或者进程不会被操作系统挂起。

3.2 阻塞IO和非阻塞IO

        通常情况下,阻塞IO和非阻塞IO就是阻塞IO调用和非阻塞IO调用。这时阻塞IO等同于阻塞IO调用,非阻塞IO等同于非阻塞IO调用。而阻塞IO调用等同于同步IO,非阻塞IO调用等同于异步IO。所以:

        阻塞IO = 阻塞IO调用 = 同步IO;

        非阻塞IO = 非阻塞IO调用 = 异步IO;

      在了解阻塞IO和非阻塞IO时,还要注意和Richard Stevens的“UNIX® Network Programming The Sockets Networking ”(UNIX网络编程)的五种IO模型中的阻塞IO模型和非阻塞IO模型区别开来。

3.2.1 阻塞IO调用和非阻塞IO调用

        阻塞IO和非阻塞IO可以表示一种调用。这时阻塞IO等同于同步IO,非阻塞IO等同于异步IO。

        阻塞IO调用:在用户进程(线程)中调用执行的时候,进程(线程)会等待该IO操作,该进程(线程)的其他操作无法执行。

        非阻塞IO调用:在用户进程(线程)中调用执行的时候,无论成功与否,该IO调用操作会立即返回,之后进程(线程)可以执行其他操作。

        https://blog.csdn.net/tjiyu/article/details/52959418

3.2.2 阻塞IO模型和非阻塞IO模型

        阻塞IO模型:

        阻塞IO进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功后,进程获取到数据。阻塞IO模型如下:

        阻塞IO模型实现难度低、开发应用较容易;适用并发量小的网络应用开发;不适用并发量大的应用:因为一个请求IO会阻塞进程,所以,得为每请求分配一个处理进程(线程)以及时响应,系统开销大。

        https://blog.csdn.net/tjiyu/article/details/52959418

        非阻塞IO模型:

        进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回进程。非阻塞IO模型如下:

        非阻塞IO模型,进程轮询(重复)调用,消耗CPU的资源;适用并发量较小、且不需要及时响应的网络应用开发。

四、同步和异步

        了解“IO”、“阻塞”和“非阻塞”概念后,再来理解“同步和异步”,就不会产生太多混淆。

        通常情况下,“同步和异步”均指“同步调用和异步调用”,“同步IO和异步IO”不会表述成“同步和异步”。

4.1 同步调用和异步调用

        首先,要有一个概念的认知:

        同步调用是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式。

       异步调用是指代码调用IO操作时,不必等待IO操作完成就返回的调用方式。

       IO操作+ 多线程/多进程,使异步的出现称为可能。

       https://github.com/calidion/calidion.github.io/issues/40

4.2 同步IO和异步IO

       同步IO和异步IO的概念,来源于UNIX网络编程的五种IO模型。其中阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型这4种为同步IO操作,只有异步IO模型是异步IO操作。

       分析上述五种模型可以发现,同步IO存在进程阻塞(有可能是等待数据(wait for data),也可能是从Kernel拷贝数据到用户态(copy data from kernel to user)),而异步IO在这两个阶段均不存在阻塞。异步IO在进程发起IO 操作后,就直接返回,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。

        所以同步IO就是阻塞IO,异步IO就是非阻塞IO。

        https://blog.csdn.net/cmustard/article/details/52818866

        https://github.com/calidion/calidion.github.io/issues/40

        这里给出《UNIX网络编程》中剩余的三种IO模型:IO复用模型、信号驱动IO模型、异步IO模型。

4.3 IO复用模型

        IO复用模型就是使用单个process同时处理多个网络连接IO的过程。其基本原理是不断的轮询所负责的所有socket,并在某个socket数据到达时,通知用户进程。使用select系统函数实现的IO复用实例如下:

        可以看到,多个进程注册IO后,只有另一个select调用进程被阻塞。

        https://blog.csdn.net/historyasamirror/article/details/5778378

        https://blog.csdn.net/tjiyu/article/details/52959418

4.4 信号驱动IO模型

        当进程发起一个IO操作时,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。

        https://blog.csdn.net/tjiyu/article/details/52959418

        基于信号驱动的IO模型常用于嵌入式等偏硬件编程,对服务器端编程不适用。

4.5 异步IO模型

         异步IO模型不等同于异步IO,不可将两者混为一谈。

        当进程发起一个IO操作,进程返回(不阻塞),但也不能返回果结;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据。

        https://blog.csdn.net/tjiyu/article/details/52959418

4.6 误区

4.6.1 同步阻塞IO、同步非阻塞IO、异步阻塞IO、异步非阻塞IO

        《UNIX网络编程》描述同步IO和异步IO时,引入了同步阻塞IO(Synchronous blocking I/O)、同步非阻塞IO(Synchronous non-blocking I/O)、异步阻塞IO(Asynchronous blocking I/O)、异步非阻塞IO(Asynchronous non-blocking I/O,AIO)等概念。正是这些概念的引入,让我们对同步、异步、阻塞、非阻塞的理解产生了混淆。如何理解这些概念呢?

       1. 上下文是IO

        我们必须清楚的认识到,以上概念均来源于对IO模型的描述,就是对五种IO模型基于同步异步和阻塞非阻塞的划分。

        同步阻塞IO就是五种模型中的阻塞IO模型,异步非阻塞IO就是异步IO模型。对于同步非阻塞IO和异步阻塞IO,因为IO多路复用的划分不定性,而产生分歧。但是可以明确的一点是,非阻塞IO模型是同步非阻塞IO。

       IO多路复用在《UNIX网络编程》里,被划分到同步非阻塞模型,而在IBM的技术博客(https://www.ibm.com/developerworks/library/l-async/),将其划分到异步阻塞IO。笔者则比较倾向于前者。首先,异步本身就是为了非阻塞,如果在异步上实现阻塞(理论上存在),没有实际价值。其次,但从IO复用模型来说,实现IO复用的进行本身执行的还是同步操作。

        https://github.com/calidion/calidion.github.io/issues/40

       2. 不可将上述术语割裂使用

        同步非阻塞IO不可看做非阻塞IO的一个情况,同步非阻塞IO是同步IO下的划分,而不是非阻塞IO的子类。所以,诸如不可将IO多路复用模型称为非阻塞IO。因为非阻塞IO就是异步IO。显然,单纯的割裂使用专业术语,会进入理解上的陷阱。

五、最后

         以上开悟,主要来源于大牛的开源博文:

        https://github.com/calidion/calidion.github.io/issues/40 ,再次感谢大牛。


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: