zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

同步,异步,阻塞,非阻塞,IO,协程,websocket

2023-04-18 14:07:19 时间

同步,异步,阻塞,非阻塞,IO

同步与异步

  • 同步就是在发出一个调用时,在没有得到结果之前,该调用就不会返回。但是一旦调用返回,就得到返回值了,换句话说,同步是指调用者主动等到这个调用的结果。
  • 异步是指在调用发出之后,这个调用就直接返回了,没有直接返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果,调用者也不会等待调用结果。而是在调用发出后,被调用者会主动通知调用者,或者通过回调函数来处理这个调用结果。

阻塞和非阻塞

  • 阻塞调用是指调用结果返回之前,调用程序会一直进行等待。
  • 非阻塞调用指在不能立刻得到结果之前,该调用虽然不会阻塞当前程序,但是调用本身还是会等待调用结果。

注意区别非阻塞,阻塞和异步。阻塞和非阻塞会等待调用结果的,异步根本就不会等待调用结果,所以异步根本就没有阻塞和非阻塞的概念。

下面的例子很好的解释了上面的概念。

比如你打电话到图书馆管理员处咨询一本书有没有库存,管理员告诉你等一下。如果你期间一直拿着电话等着,管理员查到了跟你说有,查不到就跟你说没有,那么这个过程就是同步的;如果你在等待管理员查询的过程中啥也不做(直到管理员回复你),就是阻塞的;如果在等待的过程中,你倒了杯水,上了一趟厕所,你没有闲着(程序没有挂起等待),这就是非阻塞;如果管理员告诉你他要找一会,然后就把电话挂了(没有返回结果,返回结果应该是有没有这本书。也没有等待,电话直接挂了),过一会他打电话来告诉你他找到了(被调用者通知调用者),这就是异步。

IO模型

这里统一使用Linux下的系统调用recv作为例子,它用于从套接字上接收一个消息,因为是一个系统调用,所以调用时会从用户进程空间切换到内核空间运行一段时间再切换回来。默认情况下recv会等到网络数据到达并且复制到用户进程空间或者发生错误时返回,而第4个参数flags可以让它马上返回。

阻塞IO

使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。A同学用杯子装水,打开水龙头装满水然后离开。这一过程就可以看成是使用了阻塞IO模型,因为如果水龙头没有水,他也要等到有水并装满杯子才能离开去做别的事情。很显然,这种IO模型是同步的。

非阻塞IO模型

改变flags,让recv不管有没有获取到数据都返回,如果没有数据那么一段时间后再调用recv看看,如此循环。B同学也用杯子装水,打开水龙头后发现没有水,它离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,B同学离开了装水现场(回到用户进程空间),可以做他自己的事情。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。

IO复用模型

这里在调用recv前先调用select或者poll,这2个系统调用都可以在内核准备好数据(网络数据到达内核)时告知用户进程,这个时候再调用recv一定是有数据的。因此这一过程中它是阻塞于select或poll,而没有阻塞于recv,有人将非阻塞IO定义成在读写操作时没有阻塞于系统调用的IO操作(不包括数据从内核复制到用户空间时的阻塞,因为这相对于网络IO来说确实很短暂),如果按这样理解,这种IO模型也能称之为非阻塞IO模型,但是按POSIX来看,它也是同步IO,那么也和楼上一样称之为同步非阻塞IO吧。

这种IO模型比较特别,分个段。因为它能同时监听多个文件描述符(fd)。这个时候C同学来装水,发现有一排水龙头,舍管阿姨告诉他这些水龙头都还没有水,等有水了告诉他。于是等啊等(select调用中),过了一会阿姨告诉他有水了,但不知道是哪个水龙头有水,自己看吧。于是C同学一个个打开,往杯子里装水(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于舍管阿姨会告诉C同学哪几个水龙头有水了,不需要一个个打开看(当然还有其它区别)。

信号驱动IO模型

通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。D同学让舍管阿姨等有水的时候通知他(注册信号函数),没多久D同学得知有水了,跑去装水。是不是很像异步IO?很遗憾,它还是同步IO(省不了装水的时间啊)。

异步IO模型

调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行事先指定好的函数。E同学让舍管阿姨将杯子装满水后通知他。整个过程E同学都可以做别的事情(没有recv),这才是真正的异步IO。

协程

一般在有大量IO操作业务的情况下,我们采用协程替换线程,可以到达很好的效果,一是降低了系统内存,二是减少了系统切换开销,因此系统的性能也会提升。

在协程中尽量不要调用阻塞IO的方法,比如打印,读取文件,Socket接口等,除非改为异步调用的方式,并且协程只有在IO密集型的任务中才会发挥作用。

协程只有和异步IO结合起来才能发挥出最大的威力。

websocket

http是一问一答的模式,一个请求对应一个响应;没有请求就没有响应。在没有websocket的时候,遇到那种需要较长时间处理之后才能得到响应的情况,通常采用轮询或者长轮询的方式。有了websocket之后,能够在一个TCP连接上进行全双工通信。这时候后端通过websocket可以使用异步IO来通知前端。

参考资料:

  • 作者:sheen口开河 链接:https://www.jianshu.com/p/7a5dd747e045 来源:简书
  • 作者:Euphie 链接:https://www.cnblogs.com/euphie/p/6376508.html 来源:博客园
  • 作者:王方浩 链接:https://zhuanlan.zhihu.com/p/172471249 来源:知乎