非阻塞和异步有差别吗?阻塞、非阻塞、同步和异步概念总结

阻塞与非阻塞

如阻塞、非阻塞字面意思一般,我们很容易得到结论就是:阻塞就是调用该方法后会阻塞当前线程,直到方法返回。而非阻塞则相反,调用方法后不会阻塞当前线程,会立即返回结果,比如如果当前还没有任何数据拷贝到内核缓冲区,但又由于需要立即返回结果,那么read()就会返回-1,以告知用户这次调用读取不到任何数据。

但要更准确来说的话,应该是这样:

这里有三个概念需要注意:对应的硬件内核缓冲区用户缓冲区。比如对于read(),从对应硬件拷贝数据到内核缓冲区,从内核缓冲区拷贝数据到用户缓冲区,这两个步骤都需要等待。阻塞和非阻塞针对的都是第一个步骤而言的,对于第二个步骤,不论是阻塞IO还是非阻塞IO,当前线程都会进入阻塞状态。
当然这里并不绝对,并不是一定read()都会有从内核缓冲区到用户缓冲区这一步的实现。如果没有的话,那么非阻塞IO在整个过程中就都不会阻塞了。

由上可知,阻塞和非阻塞IO的阻塞主要是对于第一个步骤进行区分的,和我们理解的阻塞与非阻塞概念不完全一致。

为什么会发生阻塞

在了解完阻塞和非阻塞的详细概念后,我们来说说为什么系统函数的调用就会阻塞线程。

首先要明确的一点是,调用操作系统提供的函数不完全都是系统调用。比如操作系统提供的操作字符串的函数,这些函数并不算是系统调用。

系统调用(system call)是用户程序与操作系统内核之间进行交互的方式,用户程序通过系统调用向操作系统内核请求一些特权操作,如文件操作、网络操作等。而操作系统提供的函数则包括两种类型:一种是直接在用户空间中运行的函数,另一种是基于系统调用封装的函数库。

我们知道操作系统分为用户态内核态,这是因为操作系统为了保证安全,会限制我们写的程序只能运行在用户态,在这个层面里,很多操作都会受限,比如说访问硬盘资源。如果说我们要访问系统资源,那么就需要切换到内核态。

我们在自己的程序中调用了系统函数,就涉及到了上下文切换。不同操作系统有不同的方式去处理系统调用,Linux采取的是一对一的方式,但这个上下文切换只涉及到从用户态切换到内核态,线程还是同一个线程。

参考链接:系统调用涉及到线程切换吗?

对于系统调用read()这种IO操作,不能立即完成,所以内核将会将该线程挂起(毕竟也是我们要求的等内容全部拷贝过来之后再继续执行),等待IO操作的完成,这样就造成了阻塞。

关于为什么系统调用会造成阻塞的详细解析可以参考这篇答案:怎样理解阻塞非阻塞与同步异步的区别?

同步与异步

同步指的是发起调用后,没返回结果前不会返回,而异步指的是发起调用后立即返回结果。

那么非阻塞的IO和异步的IO差别在哪呢?上面我们提到非阻塞IO对于读取到的数据会直接返回,那么就存在可能会返回完整的值,可能什么也没有(返回-1),而异步IO,虽然也是直接返回,但是可以通过注册回调函数等方式,告诉操作系统把需要的值完全拷贝之后,用我们注册的回调函数去处理。

非阻塞IO和异步IO确实都没有阻塞主线程,但是在返回内容上有所差异。

多路复用IO为什么算非阻塞而非异步

综上所述,多路复用IO虽然可以一个线程处理多个IO,但是它就不是通过异步告诉每个IO完成后应该执行操作,而是让操作系统去做read()操作,等需要的内容完全拷贝好之后,再通知我们去处理。

这里强烈推荐读一下这篇文章,详细阐述了多路复用IO的概念:IO多路复用到底是不是异步的?

参考链接

Linux IO模式及 select、poll、epoll详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值