精华内容
下载资源
问答
  • 同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制
    同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制同步与异步机制
    
    展开全文
  • Axios取消异步机制

    千次阅读 2020-04-08 15:31:15
    Axios取消异步机制 需要在方法前面加上async,在请求前加上await,这样执行的请求就是同步的机制

    Axios取消异步机制

    在这里插入图片描述

    需要在方法前面加上async,在请求前加上await,这样执行的请求就是同步的机制
    展开全文
  • JavaScript异步机制详解

    千次阅读 2018-01-26 15:19:16
    学习JavaScript的时候了解到JavaScript是单线程的,刚开始很疑惑,单线程怎么处理网络请求、文件读写等耗时...在介绍JavaScript的异步机制之前,首先介绍一下:什么是同步?什么是异步? 同步 如果在函数返回的时

    学习JavaScript的时候了解到JavaScript是单线程的,刚开始很疑惑,单线程怎么处理网络请求、文件读写等耗时操作呢?效率岂不是会很低?随着对这方面内容的了解和深入,知道了其中的奥秘。本篇文章就主要讲解一下JavaScript怎么处理异步问题。

    一、同步与异步

    在介绍JavaScript的异步机制之前,首先介绍一下:什么是同步?什么是异步?

    同步

    如果在函数返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的。
    如下所示:

    //在函数返回时,获得了预期值,即2的平方根
    Math.sqrt(2);
    //在函数返回时,获得了预期的效果,即在控制台上打印了'hello'
    console.log('hello');

    上面两个函数就是同步的。

    如果函数是同步的,即使调用函数执行的任务比较耗时,也会一直等待直到得到预期结果。

    异步

    如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。
    如下所示:

    //读取文件
    fs.readFile('hello.txt', 'utf8', function(err, data) {
        console.log(data);
    });
    //网络请求
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = xxx; // 添加回调函数
    xhr.open('GET', url);
    xhr.send(); // 发起函数

    上述示例中读取文件函数 readFile和网络请求的发起函数 send都将执行耗时操作,虽然函数会立即返回,但是不能立刻获取预期的结果,因为耗时操作交给其他线程执行,暂时获取不到预期结果(后面介绍)。而在JavaScript中通过回调函数 function(err, data) {console.log(data);}onreadystatechange ,在耗时操作执行完成后把相应的结果信息传递给回调函数,通知执行JavaScript代码的线程执行回调。

    如果函数是异步的,发出调用之后,马上返回,但是不会马上返回预期结果。调用者不必主动等待,当被调用者得到结果之后会通过回调函数主动通知调用者。

    二、单线程与多线程


    在上面介绍异步的过程中就可能会纳闷:既然JavaScript是单线程,怎么还存在异步,那些耗时操作到底交给谁去执行了?

    JavaScript其实就是一门语言,说是单线程还是多线程得结合具体运行环境。JS的运行通常是在浏览器中进行的,具体由JS引擎去解析和运行。下面我们来具体了解一下浏览器。

    浏览器

    目前最为流行的浏览器为:Chrome,IE,Safari,FireFox,Opera。浏览器的内核是多线程的。

    一个浏览器通常由以下几个常驻的线程:

    • 渲染引擎线程:顾名思义,该线程负责页面的渲染
    • JS引擎线程:负责JS的解析和执行
    • 定时触发器线程:处理定时事件,比如setTimeout, setInterval
    • 事件触发线程:处理DOM事件
    • 异步http请求线程:处理http请求

    需要注意的是,渲染线程和JS引擎线程是不能同时进行的。渲染线程在执行任务的时候,JS引擎线程会被挂起。因为JS可以操作DOM,若在渲染中JS处理了DOM,浏览器可能就不知所措了。

    JS引擎

    通常讲到浏览器的时候,我们会说到两个引擎:渲染引擎和JS引擎。渲染引擎就是如何渲染页面,Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。不同的引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。这里我们不做具体讨论。

    JS引擎可以说是JS虚拟机,负责JS代码的解析和执行。通常包括以下几个步骤:

    • 词法分析:将源代码分解为有意义的分词
    • 语法分析:用语法分析器将分词解析成语法树
    • 代码生成:生成机器能运行的代码
    • 代码执行

    不同浏览器的JS引擎也各不相同,Chrome用的是V8,FireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。

    之所以说JavaScript是单线程,就是因为浏览器在运行时只开启了一个JS引擎线程来解析和执行JS。那为什么只有一个引擎呢?如果同时有两个线程去操作DOM,浏览器是不是又要不知所措了。

    所以,虽然JavaScript是单线程的,可是浏览器内部不是单线程的。一些I/O操作、定时器的计时和事件监听(click, keydown…)等都是由浏览器提供的其他线程来完成的。

    三、消息队列与事件循环

    通过以上了解,可以知道其实JavaScript也是通过JS引擎线程与浏览器中其他线程交互协作实现异步。但是回调函数具体何时加入到JS引擎线程中执行?执行顺序是怎么样的?

    这一切的解释就需要继续了解消息队列和事件循环。

    如上图所示,左边的栈存储的是同步任务,就是那些能立即执行、不耗时的任务,如变量和函数的初始化、事件的绑定等等那些不需要回调函数的操作都可归为这一类。

    右边的堆用来存储声明的变量、对象。下面的队列就是消息队列,一旦某个异步任务有了响应就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和setTimeout中待执行的事件,每个异步任务都和回调函数相关联。

    JS引擎线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取消息队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务。

    JS引擎线程从消息队列中读取任务是不断循环的,每次栈被清空后,都会在消息队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫事件循环。

    上图以AJAX异步请求为例,发起异步任务后,由AJAX线程执行耗时的异步操作,而JS引擎线程继续执行堆中的其他同步任务,直到堆中的所有异步任务执行完毕。然后,从消息队列中依次按照顺序取出消息作为一个同步任务在JS引擎线程中执行,那么AJAX的回调函数就会在某一时刻被调用执行。

    四、示例

    引用一篇文章中提到的考察JavaScript异步机制的面试题来具体介绍。

    执行下面这段代码,执行后,在 5s 内点击两下,过一段时间(>5s)后,再点击两下,整个过程的输出结果是什么?

    setTimeout(function(){
        for(var i = 0; i < 100000000; i++){}
        console.log('timer a');
    }, 0)
    
    for(var j = 0; j < 5; j++){
        console.log(j);
    }
    
    setTimeout(function(){
        console.log('timer b');
    }, 0)
    
    function waitFiveSeconds(){
        var now = (new Date()).getTime();
        while(((new Date()).getTime() - now) < 5000){}
        console.log('finished waiting');
    }
    
    document.addEventListener('click', function(){
        console.log('click');
    })
    
    console.log('click begin');
    waitFiveSeconds();

    要想了解上述代码的输出结果,首先介绍下定时器。

    setTimeout的作用是在间隔一定的时间后,将回调函数插入消息队列中,等栈中的同步任务都执行完毕后,再执行。因为栈中的同步任务也会耗时,所以间隔的时间一般会大于等于指定的时间

    setTimeout(fn, 0)的意思是,将回调函数fn立刻插入消息队列,等待执行,而不是立即执行。看一个例子:

    setTimeout(function() {
        console.log("a")
    }, 0)
    
    for(let i=0; i<10000; i++) {}
    console.log("b")
    b  a

    打印结果表明回调函数并没有立刻执行,而是等待栈中的任务执行完毕后才执行的。栈中的任务执行多久,它就得等多久。

    理解了定时器的作用,那么对于输出结果就容易得出了。

    首先,先执行同步任务。其中waitFiveSeconds是耗时操作,持续执行长达5s。

    0
    1
    2
    3
    4
    click begin
    finished waiting

    然后,在JS引擎线程执行的时候,’timer a’对应的定时器产生的回调、 ‘timer b’对应的定时器产生的回调和两次 click 对应的回调被先后放入消息队列。由于JS引擎线程空闲后,会先查看是否有事件可执行,接着再处理其他异步任务。因此会产生 下面的输出顺序。

    click
    click
    timer a
    timer b

    最后,5s 后的两次 click 事件被放入消息队列,由于此时JS引擎线程空闲,便被立即执行了。

    click
    click

    参考文章
    JavaScript:彻底理解同步、异步和事件循环(Event Loop)
    从setTimeout说事件循环模型
    JavaScript单线程和异步机制
    JavaScript的单线程机制
    JavaScript单线程异步的背后——事件循环机制
    JavaScript 运行机制详解:再谈Event Loop

    展开全文
  • Linux异步机制

    千次阅读 2015-08-18 00:26:00
    Linux异步机制 还没有细细整理。 什么是异步通知:很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态, 特像硬件上常提的“中断的概念”。 比较准确的说法其实应该...

    http://m.blog.csdn.net/blog/u012491514/26276171

    Linux异步机制

    还没有细细整理。

    什么是异步通知:很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态, 特像硬件上常提的“中断的概念”。 比较准确的说法其实应该叫做“信号驱动的异步I/O”,信号是在软件层次上对中断机制的一种模拟。阻塞I/O意味着一直等待设备可访问再访问,非阻塞I/O意味着使用poll()来查询是否可访问,而异步通知则意味着设备通知应用程序自身可访问。(希望用这么一句话能表达我的意思)

    一、系统中存在的异步机制

           我认为异步机制是一种理念,并不是某一种具体实现,同步/异步的核心理解应该是如何获取消息的问题,你自身(在计算机中当然是进程本身了)亲自去获取消息,那么就是同步机制,但是如果别人使用某种方式通知你某一个消息,那么你采用的就是异步机制。内核中使用到异步机制的大概有:信号,这是一种进程间通信的异步机制;epoll,这是一种高效处理IO的异步通信机制。也就是从通信和IO两个方面通过不同的方式使用了异步机制。(可能还有别的,暂时不知道)

          下面进入正题:

    二、信号的基本概念      

           1)信号的本质

           软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

     

    收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:

    第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。

    第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。

    第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。

            在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。

           2)信号的种类

    可以从两个不同的分类角度对信号进行分类:

    可靠性方面:可靠信号与不可靠信号;

    与时间的关系上:实时信号与非实时信号。

             3)可靠信号与不可靠信号 

    Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是信号可能丢失。

     

            随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。

     

    信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。

     

    信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。

     

    对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

            4)实时信号与非实时信号

    早期Unix系统只定义了32种信号,前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。

     

    非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

          5)linux 下信号的生命周期如下:
                在目的进程中安装该信号。即是设置捕获该信号时进程进程该执行的操作码。采用signal();sigaction()系统调用来实现。
                信号被某个进程产生,同时设置该信号的目的进程(使用pid),之后交给操作系统进行管理。采用kill()、arise()、alarm()等系统调用来实现。
                信号在目的进程被注册。信号被添加进进程的PCB(task_struct)中相关的数据结构里——未决信号的数据成员。信号在进程中注册就是把信号值加入到进程的未决信号集里。
                    并且,信号携带的其他信息被保留到未决信的队列的某个sigqueue结构中。
                信号在进程中注销。在执行信号处理函数前,要把信号在进程中注销。对于非实时信号(不可靠信号),其在信号未决信号信息链中最多只有一个sigqueue结构,因此该结构被释放后,相应的信号要在未决信号集删除。而实时信号(可靠信号),如果有多个sigqueue,则不会把信号从进程的未决信号集中删除。
                信号生命的终结。进程终止当前的工作,保护上下文,执行信号处理函数,之后回复。如果内核是可抢占的,那么还需要调度。

    三、信 号 机 制

           上 一节中介绍了信号的基本概念,在这一节中,我们将介绍内核如何实现信号机制。即内核如何向一个进程发送信号、进程如何接收一个信号、进程怎样控制自己对信 号的反应、内核在什么时机处理和怎样处理进程收到的信号。还要介绍一下setjmp和longjmp在信号中起到的作用。
        1、内核对信号的基本处理方法

        内 核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看 该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检 查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

    进程的task_struct结构中有关于本进程中未决信号的数据成员:struct sigpending pending:

    struct sigpending{

            struct sigqueue *head, *tail;

            sigset_t signal;

    };

     

    第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个sigqueue类型的结构链(称之为"未决信号信息链")的首尾,信息链中的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个sigqueue结构:

    struct sigqueue{

            struct sigqueue *next;

            siginfo_t info;

    }

     

    信号在进程中注册指的就是信号值加入到进程的未决信号集sigset_t signal(每个信号占用一位)中,并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。

     

    当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失,因此,实时信号又叫做"可靠信号"。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册)。

     

    当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册(通过sigset_t signal指示),则该信号将被丢弃,造成信号丢失。因此,非实时信号又叫做"不可靠信号"。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构。

     

    总之信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)


        内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。 

         内 核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。前面介绍概念的时候讲过,处理信号有三种类型:进程接收到信号后退 出;进程忽略该信号;进程收到信号后执行用户设定用系统调用signal的函数。当进程接收到一个它忽略的信号时,进程丢弃该信号,就象没有收到该信号似 的继续运行。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创 建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时, 才返回原先进入内核的地方。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权 限)。 

    对于非实时信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),则执行完相应的处理函数后应该把信号在进程的未决信号集中删除(信号注销完毕)。否则待该信号的所有sigqueue处理完毕后再在进程的未决信号集中删除该信号。

     

    当所有未被屏蔽的信号都处理完毕后,即可返回用户空间。对于被屏蔽的信号,当取消屏蔽后,在返回到用户空间时会再次执行上述检查处理的一套流程。

         

         在信号的处理方法中有几点特别要引起注意。第一,在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设 定的对该信号的处理例程的地址,即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次使用signal系统调用。这可能会使得进程 在调用signal之前又得到该信号而导致退出。在BSD中,内核不再清除该地址。但不清除该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢 出。为了避免出现上述情况。在BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。 

          第二个要 引起注意的是,如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上,这时该信号引起进程作一次longjmp,跳出睡眠 状态,返回用户态并执行信号处理例程。当从信号处理例程返回时,进程就象从系统调用返回一样,但返回了一个错误代码,指出该次系统调用曾经被中断。这要注 意的是,BSD系统中内核可以自动地重新开始系统调用。 

         第三个要注意的地方:若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。但用户感觉不到进程曾经被唤醒,而是象没有发生过该信号一样。 

         第 四个要注意的地方:内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程检查出收到了一个子进程终止的信号时,缺省情况下,该进程 就象没有收到该信号似的,如果父进程执行了系统调用wait,进程将从系统调用wait中醒来并返回wait调用,执行一系列wait调用的后续操作(找 出僵死的子进程,释放子进程的进程表项),然后从wait中返回。SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个 信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,那么系统调用wait的动作就有所不同,因为SIGCLD的作用仅仅是唤醒一个睡眠在可被 中断优先级上的进程,那么执行wait调用的父进程被唤醒继续执行wait调用的后续操作,然后等待其他的子进程。 

       如果一个进程调用signal系统调用,并设置了SIGCLD的处理方法,并且该进程有子进程处于僵死状态,则内核将向该进程发一个SIGCLD信号。 2、setjmp和longjmp的作用 

        前面在介绍信号处理机制时,多次提到了setjmp和longjmp,但没有仔细说明它们的作用和实现方法。这里就此作一个简单的介绍。 

        在 介绍信号的时候,我们看到多个地方要求进程在检查收到信号后,从原来的系统调用中直接返回,而不是等到该调用完成。这种进程突然改变其上下文的情况,就是 使用setjmp和longjmp的结果。setjmp将保存的上下文存入用户区,并继续在旧的上下文中执行。这就是说,进程执行一个系统调用,当因为资 源或其他原因要去睡眠时,内核为进程作了一次setjmp,如果在睡眠中被信号唤醒,进程不能再进入睡眠时,内核为进程调用longjmp,该操作是内核 为进程将原先setjmp调用保存在进程用户区的上下文恢复成现在的上下文,这样就使得进程可以恢复等待资源前的状态,而且内核为setjmp返回1,使 得进程知道该次系统调用失败。这就是它们的作用。



    同步和异步:与消息的通知机制有关。

     

    本质区别

    现实例子

    同步模式

    由处理消息者自己去等待消息是否被触发

    我去银行办理业务,选择排队等,排到头了就办理。

    异步模式

    由触发机制来通知处理消息者

    我去银行办理业务,取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务。

     

    阻塞与非阻塞:与线程等待消息(无所谓同步或者异步)时的状态有关。

     

    本质区别

    现实例子

    阻塞调用

    线程挂起,不能做其他事。

    上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的。

    非阻塞调用

    线程活跃,能处理其他事。

    在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的。

     

    它们之间的组合应用举例:

     

    阻塞调用

    非阻塞调用

    同步模式

    read/write

    read/write

    O_NONBLOCK

    异步模式

    IO复用:select/poll,epoll(LT模式)

    AIO系列:aio_read,aio_write等;epoll(ET模式)

     

    同步阻塞IO

    最常用的一个模型是同步阻塞 I/O 模型。在这个模型中,用户空间的应用程序执行一个系统调用,这会导致应用程序阻塞。这意味着应用程序会一直阻塞,直到系统调用完成为止(数据传输完成或发生错误)。调用应用程序处于一种不再消费 CPU 而只是简单等待响应的状态,因此从处理的角度来看,这是非常有效的。

    图 1 给出了传统的阻塞 I/O 模型,这也是目前应用程序中最为常用的一种模型。其行为非常容易理解,其用法对于典型的应用程序来说都非常有效。在调用 read 系统调用时,应用程序会阻塞并对内核进行上下文切换。然后会触发读操作,当响应返回时(从我们正在从中读取的设备中返回),数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞(read 调用返回)。

     

    图1. 同步阻塞 I/O 模型的典型流程

    从应用程序的角度来说,read 调用会延续很长时间。实际上,在内核执行读操作和其他工作时,应用程序的确会被阻塞。

     

    同步非阻塞I/O

    同步阻塞 I/O 的一种效率稍低的变种是同步非阻塞 I/O。在这种模型中,设备是以非阻塞的形式打开的。这意味着 I/O 操作不会立即完成,read 操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN 或EWOULDBLOCK),如图 2 所示。

     

    图2. 同步非阻塞 I/O 模型的典型流程

    非阻塞的实现是 I/O 命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成(轮询)。这可能效率不高,因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。正如图 2 所示的一样,这个方法可以引入 I/O 操作的延时,因为数据在内核中变为可用到用户调用 read 返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。

     

    异步阻塞IO

    另外一个阻塞解决方案是带有阻塞通知的非阻塞 I/O。在这种模型中,配置的是非阻塞 I/O,然后使用阻塞select 系统调用来确定一个 I/O 描述符何时有操作。使 select 调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。

     

    图3 异步阻塞IO模型的典型流程(select)

    select 调用的主要问题是它的效率不是非常高。尽管这是异步通知使用的一种方便模型,但是对于高性能的I/O 操作来说不建议使用。

     

    异步非阻塞IO

    最后,异步非阻塞 I/O 模型是一种处理与 I/O 重叠进行的模型。读请求会立即返回,说明 read 请求已经成功发起了。在后台完成读操作时,应用程序然后会执行其他处理操作。当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。

     

    图4 异步非阻塞IO模型的典型流程

    在一个进程中为了执行多个 I/O 请求而对计算操作和 I/O 处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。当一个或多个 I/O 请求挂起时,CPU 可以执行其他任务;或者更为常见的是,在发起其他 I/O 的同时对已经完成的 I/O 进行操作。

    异步IO的动机  

    从前面 I/O 模型的分类中,我们可以看出 AIO 的动机。这种阻塞模型需要在 I/O 操作开始时阻塞应用程序。这意味着不可能同时重叠进行处理和 I/O 操作。同步非阻塞模型允许处理和 I/O 操作重叠进行,但是这需要应用程序根据重现的规则来检查 I/O 操作的状态。这样就剩下异步非阻塞 I/O 了,它允许处理和 I/O 操作重叠进行,包括 I/O 操作完成的通知。

    除了需要阻塞之外,select 函数所提供的功能(异步阻塞 I/O)与 AIO 类似。不过,它是对通知事件进行阻塞,而不是对 I/O 调用进行阻塞。


    展开全文
  • 同步机制与异步机制的理解 同步机制即在进行输入输出时,必须等待输入输出完毕后,才能进行后面的操作。 异步传输机制就不必等待完毕就可进行其它操作。 网络上有一个比较通俗的例子:请吃饭 同步就是我叫你...
  • ES5异步机制

    千次阅读 2018-08-31 20:10:00
    一、认识异步处理—— 从定时器开始 &lt;script&gt; setTimeout(function(){ console.log("异步"); },3000); console.log("主线程"); &lt;/script&gt; 二、基本介绍 1...
  • JS异步机制之回调函数

    千次阅读 2017-04-24 20:39:02
    今晚一边复习一遍快速学习,看到JS回调函数这里,猛然回忆起华为面试那天(到底是有多执着于华为),某帅被问到JS的异步机制是如何实现的,没答上来。 后来我自己也百度了一下,发现一种可行的方法是使用回调函数。...
  • JavaScript 异步机制

    千次阅读 2017-03-07 10:05:31
    之所以出现上面的结果,就是由于 setTimeout异步执行的结果,js具体的异步机制如下图所示: 异步执行是 javascript 最重要的一个特性,它的原理: js执行引擎只有一个主线程执行代码逻辑,遇到需要异步执行的...
  • 前面关于socket的编程,全部是基于同步机制开发的服务器和客户端,线程会进入阻塞或者挂起状态,降低线程利用率...为什么要引入异步机制? 看到过一个解释。当我们打开一个程序时,鼠标不停地转但是就是不见界面跳转,
  • android的两种异步机制

    千次阅读 2011-11-10 00:16:35
    在android中,如果涉及网络编程或者某些极耗资源的操作的时候,必然会出现操作时间久的情况 ...android提供两种异步机制: 一是使用asynctask类和使用标准线程机制 Asynctask提供一些抽象类的方
  • 事件队列会将处理事件的优先级进行排序,再通过执行栈来执行事件,由于异步会进入到异步队列中,并且异步队列需要一个等待时间才会进入到事件队列,所以通常来讲,同时处理异步和同步的时候,异步会比同步慢一些。...
  • java 异步机制与同步机制的区别

    千次阅读 2016-05-06 23:39:51
    原文:http://blog.itpub.net/17074730/viewspace-563262所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回。所以异步的同义语是非阻塞(None Blocking)。网上有很多网友用很通俗的...
  • Symbian 中使用了异步机制的有活动对象和C/S模式。其中活动对象原理: 异步函数回调RunL() 函数C/S模式原理: 服务器端回调客户端函数 多线程不属于使用了异步机制,它们只是通过分享CPU 时间片,达到共同工作的...
  • tornado异步机制浅析

    千次阅读 2016-11-12 00:51:37
    tornado异步非阻塞实现机制上用到了一个核心的语法:生成器——generator。在之前文章 python之yield(一) 中已经详细介绍了yield用法。coroutine的另一个作用就是启动生成器,调用next不断迭代,在此实现一个简单...
  • 接上篇Windows store app[Part 3]:认识WinRT的异步机制 WinRT异步机制回顾: IAsyncInfo接口:WinRT下异步功能的核心,该接口提供所有异步操作的基本功能,如标识、状态、操作。 IAsyncInfo: 1 public interface ...
  • 网上大神们关于Android消息异步机制的文章已经很多了,并且分析也很到位,我在这主要是为了记录个人体会,有错误的地方欢迎大家指正。 一.概述Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的...
  • 线程同步和异步区别 异步机制

    万次阅读 2013-07-23 09:26:17
    1 区别同步和异步 举个简单的例子 就是游戏 游戏会有图像和背景音乐 图像是由玩家操作的 而背景音乐一般都是循环播放玩家不能操作 这里的图像和声音就分别是不同的线程 图像一般是主线程 背景音乐是守护线程(守护...
  • flutter中的异步机制Future

    千次阅读 2019-06-20 10:02:00
    Dart是一个单线程语言,可以理解成物理线路中的串联,当其遇到有延迟的运算(比如IO操作、延时执行)时,线程中按顺序执行的运算就会阻塞,用户就会感觉到卡顿,于是通常用异步处理来解决这个问题。 Dart异步编程有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 393,102
精华内容 157,240
关键字:

异步机制