精华内容
下载资源
问答
  • 2、你开始烧开水,你不在那里干等着,而是去玩会手机,但是你每隔段时间会来看看水开没开,直到水开了--同步非阻塞;3、你买了个会提醒水烧开的壶,开始烧开水,你无事可干就闲着,直到水开了壶提醒你水开了--异步...

    同步/异步消息通信机制,也就是调用者如何获取结果的方式来讲的---

        同步:调用者调用方法之后不返回,直到得到结果后返回;

        异步:调用者调用方法之后不管结果,直接返回,结果是被调用者通过状态或者通知或者回调函数来告诉调用者;

    阻塞/非阻塞是对等待调用结果这段时间线程的状态来讲的---

        阻塞:调用结果返回之前,当前线程被挂起;

        非阻塞:调用结果返回之前,当前线程不被挂起,正常继续执行;


    同步/异步 和 阻塞/非阻塞 之间无关。

    举个例子:

    异步阻塞:调用一个下载函数(会开启一个下载线程,在下载线程里去下载),然后直接返回,之后当前线程挂起,直到该下载有了结果通过回调告诉当前线程成功了还是失败了,并且唤醒当前线程继续运行;


    异步结果同步的方法:synchronize和CountDownLatch

    1、通过synchronize( waitObject ){ waitObject.wait();}挂起当前线程,因为当前线程已经挂起,所以如果要唤醒当前线程,需要另外一个线程来唤醒它,可用synchronize(waitObject){waitObject.notifyAll( )或者是waitObject.notify( );}。---通常是用于简单的一个层级的结果同步;当然也可用于嵌套模式的结果同步,也就是逐级唤醒线程;

    2、通过CountDownLatch来做,在需要挂起线程的地方通过 latch.await( )的方法(latch的计数如果大于0,则线程会被挂起,否则不会),在其他线程回调返回结果的地方用latch.countDown( )来释放线程,当latch的初始值为大于1时,latch需要等所有的结果返回后被countDown( ) 为0后挂起的线程才会继续执行。---通常是用于有多个结果需要同步才唤醒线程的场景;比如,一次发起多个文件下载,多个文件下载有了结果之后才能继续后续处理,此时就可以用latch来做结果的同步。


    展开全文
  • 非阻塞 异步or 同步 关注的是 发出调用后,当前这个调用是否直接返回,不管是否有返回结果<调用的结果> 阻塞还是非阻塞关注的是:程序等待调用的结果<返回值>的状态,在无法得到返回值的情况下,调用...

    异步&同步

    • 异步or 同步 关注的是 客户端调用服务器接口后,是否有相应的调用的结果
    • 同步和异步关注的是消息通信机制
    • 所谓同步,就是在发出一个调用时,没得到结果之前,该*调用*就不返回调用者主动等待这个调用的结果
    • 所谓异步,就是在发出一个调用时,这个调用就直接返回了,不管返回有没有结果。当一个异步过程调用发出后,被调用者通过状态(回调函数)来通知调用者

    阻塞&非阻塞

    • 阻塞or 非阻塞关注的是:程序等待调用的过程,在无法得到返回值的情况下,客户端是否会阻塞当前线程而发生等待.
    • 阻塞和非阻塞关注的是程序在等待调用结果时的状态
    • 阻塞调用是指调用结果返回之前,当前线程会被挂起
    • 非阻塞调用是指在不能立即得到结果之前,该调用不会阻塞当前线程

    图解

    在这里插入图片描述

    事件驱动+同步IO的多路复用模式

    ■轮询: select, poll

    ■事件驱动: epoll有效轮询

    the Shoulders of Giant

    计算机领域中的同步(Synchronous)和异步(Asynchronous)
    怎样理解阻塞非阻塞与同步异步的区别?
    深入理解并发 / 并行,阻塞 / 非阻塞,同步 / 异步

    展开全文
  • C++之阻塞和非阻塞区别

    千次阅读 2018-06-16 21:24:47
    阻塞就是干不完不准回来, 非阻塞就是你先干,我现看看有其他事没有,完了告诉我一声我们拿最常用的sendrecv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输...

    阻塞就是干不完不准回来,   
    非阻塞就是你先干,我现看看有其他事没有,完了告诉我一声

    我们拿最常用的send和recv两个函数来说吧... 
    比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回,而对于非阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:"发送操作被阻塞了!!!你想办法处理吧..." 
    对于recv函数,同样道理,该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨,你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源....对于非阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK---"现在没有数据,回头在来看看"

    扩展:

    在进行网络编程时,我们常常见到同步、异步、阻塞和非阻塞四种调用方式。这些方式彼此概念并不好理解。下面是我对这些术语的理解。
     
    同步
        所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。最常见的例子就是 SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。
     
    异步

        异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。以 CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以向下运行。当连接真正建立起来以后,socket底 层会发送一个消息通知该对象。这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除非执行部件提供 多种选择,否则不受调用者控制。如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循 环去检查某个变量的值,这其实是一种很严重的错误)。如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知 没太多区别。

     

    阻塞
        阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上它们是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。socket接收数据的另外一个函数recv则是一个阻塞调用的例子。当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
     
    非阻塞
        非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
     
    对象的阻塞模式和阻塞函数调用
     
        对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。
    展开全文
  • 如果面试问到IO操作,这篇文章提到的问题,基本是必问,百度的面试官问我三个问题 ...(2) java IO 与 NIO(New IO)的区别 (3)select 与 epoll,poll区别 我胡乱说了一气,自己边说边觉得完蛋了。...

    https://www.cnblogs.com/aspirant/p/6877350.html

    如果面试问到IO操作,这篇文章提到的问题,基本是必问,百度的面试官问我三个问题

    (1)什么是NIO(Non-blocked IO),AIO,BIO

    (2) java IO 与 NIO(New IO)的区别

    (3)select 与 epoll,poll区别

    我胡乱说了一气,自己边说边觉得完蛋了。果然,二面没过,很简单的问题,回来后赶紧作了总结:

    一、什么是socket?什么是I/O操作?

     

    我们都知道unix(like)世界里,一切皆文件,而文件是什么呢?文件就是一串二进制流而已,不管socket,还是FIFO、管道、终端,对我们来说,一切都是文件,一切都是流。在信息 交换的过程中,我们都是对这些流进行数据的收发操作,简称为I/O操作(input and output),往流中读出数据,系统调用read,写入数据,系统调用write。不过话说回来了 ,计算机里有这么多的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd,一个fd就是一个整数,所以,对这个整数的操作,就是对这个文件(流)的操作。我们创建一个socket,通过系统调用会返回一个文件描述符,那么剩下对socket的操作就会转化为对这个描述符的操作。不能不说这又是一种分层和抽象的思想

    二、同步异步,阻塞非阻塞区别联系

        实际上同步与异步是针对应用程序与内核的交互而言的。同步过程中进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成。 异步过程中进程触发IO操作以后,直接返回,做自己的事情,IO交给内核来处理,完成后内核通知进程IO完成。

    同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。

    同步有阻塞和非阻塞之分,异步没有,它一定是非阻塞的。

    阻塞、非阻塞、多路IO复用,都是同步IO,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。真正的异步IO需要CPU的深度参与。换句话说,只有用户线程在操作IO的时候根本不去考虑IO的执行全部都交给CPU去完成,而自己只等待一个完成信号的时候,才是真正的异步IO。所以,拉一个子线程去轮询、去死循环,或者使用select、poll、epool,都不是异步。

    同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。

    异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

    阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

    非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。

    我认为, 同步与异步的根本区别是:

    (1) 这是 BIO,同步阻塞的模型,下面也有,

    由上面的图可以看出,IO读分为两部分,(a)是数据通过网关到达内核,内核准备好数据,(b)数据从内核缓存写入用户缓存。

    同步:不管是BIO,NIO,还是IO多路复用,第二步数据从内核缓存写入用户缓存一定是由 用户线程自行读取数据,处理数据。

    异步:第二步数据是内核写入的,并放在了用户线程指定的缓存区,写入完毕后通知用户线程。

     

     

    二、阻塞?

    什么是程序的阻塞呢?想象这种情形,比如你等快递,但快递一直没来,你会怎么做?有两种方式:

    • 快递没来,我可以先去睡觉,然后快递来了给我打电话叫我去取就行了。
    • 快递没来,我就不停的给快递打电话说:擦,怎么还没来,给老子快点,直到快递来。

    很显然,你无法忍受第二种方式,不仅耽搁自己的时间,也会让快递很想打你。
    而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

    • 非阻塞忙轮询:数据没来,进程就不停的去检测数据,直到数据来。
    • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理。

    先说说阻塞,因为一个线程只能处理一个套接字的I/O事件,如果想同时处理多个,可以利用非阻塞忙轮询的方式,伪代码如下: 

    复制代码

    while true  
    {  
        for i in stream[]  
        {  
            if i has data  
            read until unavailable  
        }  
    }  

    复制代码

    我们只要把所有流从头到尾查询一遍,就可以处理多个流了,但这样做很不好,因为如果所有的流都没有I/O事件,白白浪费CPU时间片。正如有一位科学家所说,计算机所有的问题都可以增加一个中间层来解决,同样,为了避免这里cpu的空转,我们不让这个线程亲自去检查流中是否有事件,而是引进了一个代理(一开始是select,后来是poll),这个代理很牛,它可以同时观察许多流的I/O事件,如果没有事件,代理就阻塞,线程就不会挨个挨个去轮询了,伪代码如下:  

    复制代码

    while true  
    {  
        select(streams[]) //这一步死在这里,知道有一个流有I/O事件时,才往下执行  
        for i in streams[]  
        {  
            if i has data  
            read until unavailable  
        }  
    }  

    复制代码

     但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

    epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))伪代码如下:

    复制代码

    while true  
    {  
        active_stream[] = epoll_wait(epollfd)  
        for i in active_stream[]  
        {  
            read or write till  
        }  
    }  

    复制代码

     

    可以看到,select和epoll最大的区别就是:select只是告诉你一定数目的流有事件了,至于哪个流有事件,还得你一个一个地去轮询,而epoll会把发生的事件告诉你,通过发生的事件,就自然而然定位到哪个流了。不能不说epoll跟select相比,是质的飞跃,我觉得这也是一种牺牲空间,换取时间的思想,毕竟现在硬件越来越便宜了。

    更详细的Select,poll,epoll 请参考:select、poll、epoll之间的区别(搜狗面试)

    三、I/O多路复用

    好了,我们讲了这么多,再来总结一下,到底什么是I/O多路复用。
    先讲一下I/O模型:
    首先,输入操作一般包含两个步骤:

    1. 等待数据准备好(waiting for data to be ready)。对于一个套接口上的操作,这一步骤关系到数据从网络到达,并将其复制到内核的某个缓冲区。
    2. 将数据从内核缓冲区复制到进程缓冲区(copying the data from the kernel to the process)。

    其次了解一下常用的3种I/O模型:

    1、阻塞I/O模型(BIO)

    最广泛的模型是阻塞I/O模型,默认情况下,所有套接口都是阻塞的。 进程调用recvfrom系统调用,整个过程是阻塞的,直到数据复制到进程缓冲区时才返回(当然,系统调用被中断也会返回)。

    2、非阻塞I/O模型(NIO)

    当我们把一个套接口设置为非阻塞时,就是在告诉内核,当请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。当数据没有准备好时,内核立即返回EWOULDBLOCK错误,第四次调用系统调用时,数据已经存在,这时将数据复制到进程缓冲区中。这其中有一个操作时轮询(polling)。

    3、I/O复用模型

    此模型用到select和poll函数,这两个函数也会使进程阻塞,select先阻塞,有活动套接字才返回,但是和阻塞I/O不同的是,这两个函数可以同时阻塞多个I/O操作,而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写(就是监听多个socket)。select被调用后,进程会被阻塞,内核监视所有select负责的socket,当有任何一个socket的数据准备好了,select就会返回套接字可读,我们就可以调用recvfrom处理数据。
    正因为阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用。

     

    4、信号驱动I/O模型(signal driven I/O, SIGIO)

      首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它来读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达(第一阶段)期间,进程可以继续执行,不被阻塞。免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

     

    5、异步I/O模型(AIO, asynchronous I/O)

      进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

      这个模型工作机制是:告诉内核启动某个操作,并让内核在整个操作(包括第二阶段,即将数据从内核拷贝到进程缓冲区中)完成后通知我们。

    这种模型和前一种模型区别在于:信号驱动I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成。

     

     

    高性能IO模型浅析 

     

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:

    (1)同步阻塞IO(Blocking IO):即传统的IO模型。

    (2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。

    (3)IO多路复用(IO Multiplexing):即经典的Reactor设计模式,Java中的Selector和Linux中的epoll都是这种模型。

    (4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。 

    为了方便描述,我们统一使用IO的读操作作为示例。

     

    一、同步阻塞IO

     

    同步阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞。

    图1 同步阻塞IO

    如图1所示,用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作。

    用户线程使用同步阻塞IO模型的伪代码描述为:

    复制代码

    {
    
    read(socket, buffer);
    
    process(buffer);
    
    }

    复制代码

     

    即用户需要等待read将socket中的数据读取到buffer后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够。

     

    二、同步非阻塞IO

     

    同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NONBLOCK。这样做用户线程可以在发起IO请求后可以立即返回。

     

    图2 同步非阻塞IO

    如图2所示,由于socket是非阻塞的方式,因此用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。

    用户线程使用同步非阻塞IO模型的伪代码描述为:

    复制代码

    {
    
    while(read(socket, buffer) != SUCCESS)
    
    ;
    
    process(buffer);
    
    }

    复制代码

     

    即用户需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。

     

    三、IO多路复用

    IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。

    图3 多路分离函数select

    如图3所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。

    从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

    用户线程使用select函数的伪代码描述为:

    复制代码

    {
    
    select(socket);
    
    while(1) {
    
    sockets = select();
    
    for(socket in sockets) {
    
    if(can_read(socket)) {
    
    read(socket, buffer);
    
    process(buffer);
    
    }
    
    }
    
    }
    
    }

    复制代码

     

    其中while循环前将socket添加到select监视中,然后在while内一直调用select获取被激活的socket,一旦socket可读,便调用read函数将socket中的数据读取出来。

     

    然而,使用select函数的优点并不仅限于此。虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。

    IO多路复用模型使用了Reactor设计模式实现了这一机制。

    图4 Reactor设计模式

    如图4所示,EventHandler抽象类表示IO事件处理器,它拥有IO文件句柄Handle(通过get_handle获取),以及对Handle的操作handle_event(读/写等)。继承于EventHandler的子类可以对事件处理器的行为进行定制。Reactor类用于管理EventHandler(注册、删除等),并使用handle_events实现事件循环,不断调用同步事件多路分离器(一般是内核)的多路分离函数select,只要某个文件句柄被激活(可读/写等),select就返回(阻塞),handle_events就会调用与文件句柄关联的事件处理器的handle_event进行相关操作。

    图5 IO多路复用

    如图5所示,通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。

    用户线程使用IO多路复用模型的伪代码描述为:

    复制代码

    void UserEventHandler::handle_event() {
    
    if(can_read(socket)) {
    
    read(socket, buffer);
    
    process(buffer);
    
    }
    
    }
    
     
    
    {
    
    Reactor.register(new UserEventHandler(socket));
    
    }

    复制代码

     

    用户需要重写EventHandler的handle_event函数进行读取数据、处理数据的工作,用户线程只需要将自己的EventHandler注册到Reactor即可。Reactor中handle_events事件循环的伪代码大致如下。

    复制代码

    Reactor::handle_events() {
    
    while(1) {
    
    sockets = select();
    
    for(socket in sockets) {
    
    get_event_handler(socket).handle_event();
    
    }
    
    }
    
    }

    复制代码

     

    事件循环不断地调用select获取被激活的socket,然后根据获取socket对应的EventHandler,执行器handle_event函数即可。

    IO多路复用是最常使用的IO模型,但是其异步程度还不够“彻底”,因为它使用了会阻塞线程的select系统调用。因此IO多路复用只能称为异步阻塞IO,而非真正的异步IO。

     

    四、异步IO

     

    “真正”的异步IO需要操作系统更强的支持。在IO多路复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可。

    异步IO模型使用了Proactor设计模式实现了这一机制。

    图6 Proactor设计模式

    如图6,Proactor模式和Reactor模式在结构上比较相似,不过在用户(Client)使用方式上差别较大。Reactor模式中,用户线程通过向Reactor对象注册感兴趣的事件监听,然后事件触发时调用事件处理函数。而Proactor模式中,用户线程将AsynchronousOperation(读/写等)、Proactor以及操作完成时的CompletionHandler注册到AsynchronousOperationProcessor。AsynchronousOperationProcessor使用Facade模式提供了一组异步操作API(读/写等)供用户使用,当用户线程调用异步API后,便继续执行自己的任务。AsynchronousOperationProcessor 会开启独立的内核线程执行异步操作,实现真正的异步。当异步IO操作完成时,AsynchronousOperationProcessor将用户线程与AsynchronousOperation一起注册的Proactor和CompletionHandler取出,然后将CompletionHandler与IO操作的结果数据一起转发给Proactor,Proactor负责回调每一个异步操作的事件完成处理函数handle_event。虽然Proactor模式中每个异步操作都可以绑定一个Proactor对象,但是一般在操作系统中,Proactor被实现为Singleton模式,以便于集中化分发操作完成事件。

    图7 异步IO

    如图7所示,异步IO模型中,用户线程直接使用内核提供的异步IO API发起read请求,且发起后立即返回,继续执行用户线程代码。不过此时用户线程已经将调用的AsynchronousOperation和CompletionHandler注册到内核,然后操作系统开启独立的内核线程去处理IO操作。当read请求的数据到达时,由内核负责读取socket中的数据,并写入用户指定的缓冲区中。最后内核将read的数据和用户线程注册的CompletionHandler分发给内部Proactor,Proactor将IO完成的信息通知给用户线程(一般通过调用用户线程注册的完成事件处理函数),完成异步IO。

    用户线程使用异步IO模型的伪代码描述为:

    复制代码

    void UserCompletionHandler::handle_event(buffer) {
    
    process(buffer);
    
    }
    
     
    
    {
    
    aio_read(socket, new UserCompletionHandler);
    
    }

    复制代码

     

    用户需要重写CompletionHandler的handle_event函数进行处理数据的工作,参数buffer表示Proactor已经准备好的数据,用户线程直接调用内核提供的异步IO API,并将重写的CompletionHandler注册即可。

    相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式(IO事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定的缓冲区中)。Java7之后已经支持了异步IO,感兴趣的读者可以尝试使用。

    展开全文
  • 主要介绍了JAVA同步、异步、阻塞和非阻塞之间的区别,文中讲解非常细致,帮助大家更好的理解学习,感兴趣的朋友可以了解下
  • socket阻塞和非阻塞

    万次阅读 多人点赞 2018-09-27 20:45:22
    一、socket阻塞和非阻塞有哪些不同 1. 建立连接 阻塞方式下,connect首先发送SYN请求到服务器,当客户端收到服务器返回的SYN的确认时,则connect返回,否则的话一直阻塞。 非阻塞方式,connect将启用TCP协议的三...
  • 原因可能是因为同步/异步阻塞/非阻塞是分不开的,而常见的情况是 同步阻塞 异步非阻塞 ,所以容易把同步阻塞混淆,把异步和非阻塞混淆。 其实,同步包括了 同步阻塞 同步非阻塞 ,异步也包括了 异步阻塞 ...
  • Linux 阻塞和非阻塞 IO简介阻塞和非阻塞简介1、阻塞IO2、非阻塞IO 阻塞和非阻塞简介 这里的 IO 指的是 Input/Output,也就是输入/输出,是应用程序对驱动设备的输入/输出操作。当应用程序对设备驱动进行操作的时候,...
  • 阻塞(=)和非阻塞(<=)语句的最主要的区别在其后的引用它的语句的电路结构上: 1)、对于阻塞语句,其综合出的组合逻辑的输出; 2)、对于非阻塞语句,其综合出的触发器的输出。 一般来说时序逻辑always模块...
  • 阻塞和非阻塞io区别

    2014-05-01 15:19:40
    IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv...非阻塞IO 阻塞IO:  在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:  基本概念
  • Java之阻塞和非阻塞以及同步异步的区别

    万次阅读 多人点赞 2018-05-16 10:47:22
    转载:https://www.cnblogs.com/George1994/p/6702084.html阻塞和非阻塞,同步异步1 例子故事:老王烧开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。老王想了想,有好几种等待...
  • 同步异步、阻塞和非阻塞 首先说明我对这些概念也不是很清楚,以下内容是我做的一些理事。 同步异步、阻塞和非阻塞这是两组概念,说的是不同的事情,同步阻塞没有必然的联系,异步和非阻塞也没有必然的...
  • 文章目录操作系统I/O:阻塞非阻塞调用阻塞I/O时,应用程序需等待I/O完成才返结果调用非阻塞I/O为调用之后立即返回,轮询技术主要有以下四种:selectpollepollkequeue参考链接阻塞非阻塞系统调用阻塞非阻塞参考...
  • 阻塞和非阻塞区别

    2016-09-02 00:13:25
    阻塞:阻塞就是指你必须把这件事干完,干不完就得一直等着干完没等干完返回结果才能走 非阻塞:想做这事情的时候,需要很久,这时候可以去干其他事情,等那个人干完了之后就有结果了,他会告诉你,你可以去干了。
  • 阻塞和非阻塞

    千次阅读 2018-07-02 23:10:16
    在介绍阻塞和非阻塞之前,我们先来了解一下多线程间一个重要的概念——临界区。临界区——一种公有的资源或者共享数据,它可以被多个线程使用。临界区资源一次只能被一个线程使用,其它线程必须等待上一个线程执行...
  • 同步、异步,阻塞、非阻塞,希望这几张图能帮你理清思路。
  • 理解同步/异步阻塞/非阻塞区别

    千次阅读 多人点赞 2017-03-30 09:37:04
    同步/异步阻塞/非阻塞区别...
  • 同步阻塞,同步非阻塞,异步阻塞,异步非阻塞区别和关系
  • 同步/异步 阻塞/非阻塞区别

    千次阅读 2017-02-21 17:38:03
    同步 异步 阻塞 非阻塞
  • 阻塞就是干不完不准回来,  组赛就是你先干,我现看看有其他事没有,完了告诉我一声 我们拿最常用的sendrecv两个函数来说吧...  比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据...
  • nodeJS阻塞和非阻塞

    千次阅读 2018-08-02 09:30:35
    2、阻塞 (1)创建一个名为demo2.js的文件 (2)输入代码: var fs = require("fs"); var data = fs.readFileSync('text.txt'); console.log(data.toString()); console.log("程序执行结束...
  • 阻塞队列和非阻塞队列

    千次阅读 2018-06-21 14:27:56
    1.阻塞队列和非阻塞队列的区别:阻塞队列可以阻塞,非阻塞队列不能阻塞,只能使用队列wait(),notify()进行队列消息传送。而阻塞队列当队列里面没有值时,会阻塞直到有值输入。输入也一样,当队列满的时候,会阻塞,...
  • 非阻塞IO 阻塞IO:  在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:  基本概念:  阻塞IO::  socket 的阻塞模式意味着必须要做完IO 操作(包括错误)...
  • 阻塞非阻塞阻塞非阻塞区别 阻塞 为了完成一个功能,发起一个调用,如果不具备条件的话则一直等待,直到具备条件则完成 非阻塞 为了完成一个功能,发起一个调用,具备条件直接输出,不具备条件直接报错返回 区别 其实就...
  • 通常我们理解的阻塞,是阻塞了某个线程,即代码执行到这里后等待这个函数块执行完毕,才能继续向下运行。比如常见的控制台输入函数,必须要求用户输入一个值才能继续,否则等待。 在Android里,一部分人对阻塞做了...
  • 关于socket通信原理的解释,详细的解释了阻塞通信和非阻塞通信的区别
  • TCPUDP阻塞和非阻塞之间的区别

    千次阅读 2018-10-18 17:04:00
    首先socket在默认情况下是阻塞状态的,这就使得发送以及接收操作... 首先需要说明的是,不管阻塞还是非阻塞,在发送时都会将数据从应用缓冲区拷贝到内核缓冲区(SO_RCVBUF选项声明,除非缓冲区大小为0)。我在网络...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 425,344
精华内容 170,137
关键字:

阻塞和非阻塞的区别