zoukankan      html  css  js  c++  java
  • 阻塞、非阻塞、同步、异步之个人理解

    阻塞、非阻塞、同步、异步

    先抛观点:

    对于一个纯粹的 IO 模型而言,不存在异步阻塞的 IO 模型,这两个词搭在一起就是有歧义的。

    如果我们把整个程序开发过程中的角色分成两类:调用者和被调用者,这样就能很好地理解关于 IO 模型那些概念。

    调用者:写程序的人,这个可以映射成所有已知细节的函数(确实知道函数的细节,或者说函数是这个人实现的)。

    被调用者:给调用者提供调用函数的人,这个可以映射成所有对于调用者而言未知细节或不愿知道细节的函数(无法获知或不关心)。

    一个程序必然由调用者和被调用者构成,他们可能会有交互,也有可能不会有交互(这种情况不存在被调用者)。

    同步:

    调用者询问被调用者,被调用者对于询问的内容给于响应。

    具体体现:

    对于一个由自己实现的函数 A内部(以 C 为例,任何一个编码过程几乎都是在实现函数)(因为这个函数是自己写的所以调用者视角),尝试调用未知细节或不愿知道细节的函数B,不论如何结果都会以函数返回值的形式呈现在函数 A 内。

    阻塞:

    陷入系统调用,系统发现 IO 会在很长时间后完成,选择挂起当前进程,将 CPU 控制权调度给其他进程。表象为,某个被调用者得不到第一时间的返回。

    非阻塞:

    类似于阻塞,就很好理解,系统调用会马上返回。但它不一定会返回调用者期望的数据,因为数据必定没有那么快准备好。那么在非阻塞这个概念成立的时候,必然是同步的获取信息方式,因为信息是以被调用的未知细节的函数的返回值的形式呈现给调用者的。

    那么我们讨论阻塞和非阻塞的时候实际更多的是在关心什么呢?

    操作系统切换进程的开销,阻塞的时候 CPU 并没有闲下来而是交给了别的调用者去使用(不过保存和切换上下文的时间和资源被消耗了),而非阻塞的时候 CPU 也没有停下来,只不过他可能会因为当前调用者(已经实现的业务函数)的CPU 计算需求并不充足,而导致 CPU 被无意义的忙循环给浪费,其他调用者得不到充分利用 CPU。

    异步:

    调用者将询问请求注册给被调用者,被调用者并不响应数据。被调用者,会在一个对调用者而言不知情的时间通知调用者。

    具体体现:

    对于一个由自己实现的函数 A内部(以 C 为例,任何一个编码过程几乎都是在实现函数)(因为这个函数是自己写的所以调用者视角),尝试调用未知细节或不愿知道细节的函数B,告诉B 自己的请求。B回应并不以返回值回应,B 会在某个 A 不知情的时刻调用 A 之前提供过的函数指针C,并将信息数据以参数或全局变量的形式呈现在C 内。因为异步通常是以操作系统对信号的回调处理呈现的,所以大多数异步过程是可以被抢占的。

    这样就很明朗了,如果我们以一个纯粹的角度去看待一段 IO 处理的代码,被看待的一方(被调用者)是纯粹的系统调用。那么异步阻塞是必然不存在的,因为异步 API 本身不会因为某种原因阻塞。

    但是如果我们用一个更宽阔的眼光去看待一段被封装过的 IO 处理代码,例如(伪代码):

    // NewRead 内部的 readAsynBlock 方法的定义
    NewRead.readAsynBlock(){ while(1){
    // 假定read_without_block 是非阻塞读的 API
    if(a = read_without_block()){   this.handler(a);
      return; } } func A{ newRead
    .handler = func(data){echo data} a = newRead.readAsynBlockread(); }

    这样的处理方式,如果对于 A 函数而言它屏蔽了或不关心 newRead.read(),这就是异步阻塞式的。如果 A 对于 newRead.read()是知情的,则是同步非阻塞式的。

    再如果,被调用者在 readAsynNoBlock 内部添加定时器,定时调用非阻塞 read API,有正返回值则回调内容。这对于一切都不知情的 A 而言,就成了异步非阻塞的 IO 模型。

    // NewRead 内部的 readAsynNoBlock 方法的定义
    NewRead.readAsynNoBlock(){
    // 如果没有添加过定时器
    if(!already_add)
    // 添加定时器100毫秒执行一次操作 Timer
    .add(func(newRead){ if(a = read_without_block()){   this.handler(a);   return; } },this,100); } func A{ newRead.handler = func(data){echo data} a = newRead.readAsynNoBlockread(); echo 123; }

    所以对于不同的调用者和被调用者的语境,IO 模型是可以互相转化的,被调用者对相对底层的 IO 模型的封装可以改变调用者的编程方式。但对于性能而言,或许从纯粹的角度去描述 IO 模型是更有意义的。

  • 相关阅读:
    第9天 图片整合
    第六天 元素类型
    第五天 文本溢出
    第四天 盒子模型
    第三天 css核心属性
    第二天 css基础 ,部分选择符
    第一天 HTML基础
    *Move Zeroes
    Word Pattern
    ReentrantLock
  • 原文地址:https://www.cnblogs.com/TsAihS/p/6901742.html
Copyright © 2011-2022 走看看