JavaIO同步异步与阻塞非阻塞的区别讲解
- 格式:doc
- 大小:67.50 KB
- 文档页数:10
常⽤4种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设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
(4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步⾮阻塞IO。
(5)基于信号驱动的IO(Signal Driven IO)模型,由于该模型并不常⽤(略)在理解关于同步和阻塞的概念前,需要知道I/0操作主要分成两部分①数据准备,将数据加载到内核缓存(数据加载到操作系统)②将内核缓存中的数据加载到⽤户缓存(从操作系统复制到应⽤中)同步和异步的概念描述的是⽤户线程与内核的交互⽅式阻塞和⾮阻塞的概念描述的是⽤户线程调⽤内核IO操作的⽅式异步就是异步来源:同步和异步针对应⽤程序来,关注的是程序中间的协作关系;阻塞与⾮阻塞更关注的是单个进程的执⾏状态。
阻塞、⾮阻塞、多路IO复⽤,都是同步IO,异步必定是⾮阻塞的,所以不存在异步阻塞和异步⾮阻塞的说法。
真正的异步IO需要CPU的深度参与。
换句话说,只有⽤户线程在操作IO的时候根本不去考虑IO的执⾏全部都交给CPU去完成,⽽⾃⼰只等待⼀个完成信号的时候,才是真正的异步IO。
所以,拉⼀个⼦线程去轮询、去死循环,或者使⽤select、poll、epool,都不是异步。
PS:1,异步是⼀个相对概念,实际应⽤中没有绝对的异步,现实中更多称为“异步”只是代表阻塞。
2,不同场合,语⾔环境,概念不⼀样,有时候同步就代表了阻塞,异步表⽰⾮阻塞。
Java多线程、同步异步及阻塞和⾮阻塞1、进程和线程的概念进程:运⾏中的应⽤程序称为进程,拥有系统资源(cpu、内存)线程:进程中的⼀段代码,⼀个进程中可以有多段代码。
本⾝不拥有资源(共享所在进程的资源);在java中,程序⼊⼝被⾃动创建为主线程,在主线程中可以创建多个⼦线程。
多进程: 在操作系统中能同时运⾏多个任务(程序)多线程: 在同⼀应⽤程序中有多个功能流同时执⾏已经有了进程,为什么还会需要线程呢?主要原因如下:许多应⽤程序中,同时发⽣着多个活动。
将这些应⽤程序分解成多个准并⾏的线程,程序设计的模型会变成更加简单。
由于线程⽐进程进⾏更加轻量,创建和取消更加容易。
如果程序是IO密集型,那么多线程执⾏能够加快程序的执⾏速度。
(如果是CPU密集型,则没有这个优势)在多CPU系统中,多线程是可以真正并⾏执⾏的。
2、线程的主要特点①、不能以⼀个⽂件名的⽅式独⽴存在在磁盘中;②、不能单独执⾏,只有在进程启动后才可启动;③、线程可以共享进程相同的内存(代码与数据)。
3、多线程原理同⼀时间,CPU只能处理1条线程,只有1条线程在⼯作(执⾏)多线程并发(同时)执⾏,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间⾜够快,就造成了多线程并发执⾏的假象思考:如果线程⾮常⾮常多,会发⽣什么情况?CPU会在N多线程之间调度,CPU会累死,消耗⼤量的CPU资源每条线程被调度执⾏的频次会降低(线程的执⾏效率降低)4、线程的主要⽤途①、利⽤它可以完成重复性的⼯作(如实现动画、声⾳等的播放)。
②、从事⼀次性较费时的初始化⼯作(如⽹络连接、声⾳数据⽂件的加载)。
③、并发执⾏的运⾏效果(⼀个进程多个线程)以实现更复杂的功能5、多线程(多个线程同时运⾏)程序的优缺点优点:①、可以减轻系统性能⽅⾯的瓶颈,因为可以并⾏操作;②、提⾼CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提⾼了任务管理的灵活性;另⼀⽅⾯,在多CPU系统中,可以把不同的线程在不同的CPU中执⾏,真正做到同时处理多任务。
阻塞非阻塞同步异步
阻塞、非阻塞、同步、异步是计算机处理任务时会遇到的四种基本模式。
它们是两两对立,但每种模式都有各自的应用场景,有助于我们
更有效地完成复杂的任务。
下面,我们将分步骤详细介绍。
首先,我们来看阻塞和非阻塞。
阻塞指的是程序在执行其他任务之前
必须等待当前任务完成,而在这期间程序将不能执行其他任务。
相反,非阻塞模式则是程序在执行其他任务时可以继续执行其他任务,而不
必等待当前任务完成。
阻塞模式适用于执行任务时需要依赖其他任务
的情况,而非阻塞模式更适合于某些不依赖任务的情况。
其次,我们来看同步和异步。
同步指的是多个任务按照顺序进行,每
个任务的最后结果都必须等待上一个任务完成之后才能进行下一个任务,而在此过程中,一旦遇到阻塞,整个流程就会被中断,从而导致
程序执行效率很低。
异步模式则是在处理多个任务时,不必按照顺序
处理,任务之间可以并发进行,即使某个任务出现阻塞,仍不影响其
他任务的执行,从而使程序执行效率更高。
最后,从上面的分析可以看出,阻塞、非阻塞、同步、异步是计算机
处理任务时会遇到的四种基本模式,它们有助于我们更有效地处理复
杂的任务。
一般来说,阻塞模式更适合于任务依赖的情况,而非阻塞
模式更适合于任务不依赖的情况;同步模式适合于计算密集型的情况,而异步模式则更适合于IO密集型的情况。
同步与异步IO、阻塞与非阻塞IO学习各种高级外挂制作技术,马上去百度搜索"魔鬼作坊",点击第一个站进入,快速成为做挂达人。
很多时候我们常常看到同步与异步,阻塞与非阻塞的出现。
有的地方直接将同步与阻塞画上了等号。
异步与非阻塞画上了等号。
事实上这是不对的。
同步不等于阻塞,而异步也不等于非阻塞。
下面就来仔细的看看同步与异步、阻塞与非阻塞的概念差别,及他们的组合应用。
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
按照这个定义,其实绝大多数函数都是同步调用(例如sin,isdigit等)。
但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
最常见的例子就是SendMessage。
该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者异步:异步的概念和同步相对。
当一个异步过程调用发出后,调用者不能立刻得到结果。
实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
以CAsycSocket 类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行。
当连接真正建立起来以后,socket底层会发送一个消息通知该对象。
这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。
可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。
如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误)。
如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。
至于回调函数,其实和通知没太多区别阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起。
同步(synchronous)IO和异步(asynchronous)IO,阻塞(blocking)IO和非阻塞(non-blocking)IO先看一个较低的层次。
如果从CPU的角度看,其实大部分的IO都是异步的:因为CPU启动这个IO操作后,就去干其它的事情了,一直到产生一个中断,告诉它IO完成了。
“Most physical I/O is asynchronous—the CPU starts the transfer and goes off to do something else until the interrupt arrives. User programs are much easier to write if the I/O operations are blocking—after a read system call the program is automatically suspended until the data are available in the buffer. It is up to the operating system to make operations that are actually interrupt-driven look blocking to the user programs.”(引自Modern Operating Systems, 2ed)不过,本文并不想探究那么底层的东东。
作为程序员,更多的还是从应用层面来考虑。
所以,以下重点介绍的是应用程序中能够采用的四种IO机制。
关于图的解释,首先要清楚理解一个任务线:Application是一个程序或者执行一个逻辑的函数,其中当它执行到Read()方法的时候,就会通过I/O操作从服务器端的内核(Kernel,也可能是逻辑方法)获取数据(initiate),当数据从服务器端回到本地后,通过Read response 组装成Application所需要的数据,然后执行application接下来要做的事情。
同步、异步、阻塞、非阻塞、并发、并行同步同步就是发起一个请求,直到请求返回结果之后,才进行下一步操作。
简单来说,同步就是必须一件事一件事的做,等前一件做完了,才能做下一件事。
异步当一个异步操作发出后,调用者在没有得到结果之前,可以继续执行后续操作。
这就是异步。
同步和异步的区别:请求发出后,是否需要等待请求结果,才能继续执行其他操作。
阻塞在调用结果返回之前,当前线程会被挂起。
调用线程只有在得到结果之后才会被唤醒执行后续的操作。
非阻塞在结果没有返回之前,该调用不会阻塞住当前线程。
举例说明任务:用水壶烧水,烧开后就去泡咖啡。
方式一同步阻塞:用水壶烧水,并且站在煤气灶旁边,啥事不干,两眼直勾勾的盯着水壶,等水烧开。
烧开后就去泡咖啡。
方式二同步非阻塞:用水壶煮水,不过此时不再傻傻得站在那里看水开没开,而是去玩局LOL,每当自己死了,就过来看看水开了没有。
如果水开了就去泡咖啡。
方式三异步阻塞:用响壶烧水,仍然站在煤气灶旁边,不过此时不两眼直勾勾的盯着壶了,而是听响,因为响壶水开时会用响声通知小A。
方式四异步非阻塞:用响壶烧水,然后是去玩局LOL,等听到响壶的声音提醒后,再去跑咖啡。
python 的tornado 框架就是一种异步非阻塞框架。
并发:并发:指应用能够交替执行不同的任务,当有多个线程在一个CPU运行,操作系统只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,不同时间段,快速切换不同的线程代码运行。
例子:吃一口饭喝一口水并行:指应用能够同时执行不同的任务系统有多个CPU,执行多个线程,可以一个CPU执行一个线程,线程之间互不抢占CPU 资源,可以同时进行。
例子:边听歌边上网。
阻塞式I0和⾮阻塞式IO同步异步详细介绍请求描述:`阻塞/⾮阻塞` 和 `同步/异步` 不是⼀个概念。
举⼏个简单的例⼦。
当进程调⽤⼀个进⾏IO操作的API时(⽐如read函数),在数据没有到达前,read 会挂起,进程会卡住。
在数据读取完毕返回给进程时,read 返回(返回值为读取到的字节数,数据从内核拷贝到⽤户空间),然后进程继续执⾏。
那么这次 read 调⽤,是阻塞的。
⾮阻塞就是 read 在数据没有读取完毕前,就返回了(返回值为-1,errno 设置为 EAGAIN)。
此时进程没有拿到需要的数据。
那怎么办?有两种办法。
⼀种是同步:因为进程没办法知道数据什么时候才真正读取完毕了,所以需要每隔⼀段时间就去轮询⼀下(就是重新调⽤ read,看是不是数据真的已经读取完毕了)。
⼤部分场景中都不会使⽤这种⽅式。
但在某些特殊的情况下效率会特别⾼。
⼀种是异步:早期的异步实现⽅式是内核给进程发信号(SIGIO 或者 SIGPOLL)。
数据读写完毕后,内核发信号给进程,然后进程内的信号处理函数再调⽤ read 读取数据(这时可以确保数据真的已经读取完毕了)。
但这种⽅式有⼀个⼩⼩的瑕疵,就是在进程进⾏多个 fd 读写的时候,信号来的时候没办法分清到底是哪个 fd 上的数据已经真正准备好了。
所以进程还是要对所有持有的 fd 进⾏ read 调⽤。
后来的异步实现,就有了更好的 select / poll / epoll(I/O multiplexing)。
现在基本上像⽐较流⾏的 Nginx / Redis 都⽤ epoll(在 FreeBSD 上是 kqueue)图解:阻塞式I/O模型:默认情况下,所有套接字都是阻塞的。
怎么理解?先理解这么个流程,⼀个输⼊操作通常包括两个不同阶段:(1)等待数据准备好;(2)从内核向进程复制数据。
对于⼀个套接字上的输⼊操作,第⼀步通常涉及等待数据从⽹络中到达。
当所有等待分组到达时,它被复制到内核中的某个缓冲区。
阻塞IO和⾮阻塞IO,同步IO和异步IO⼀、阻塞io我这个进程调⽤了⼀个功能需要磁盘io,那么我整个进程就会被阻塞住,在做完磁盘io之前我都不能动。
当内核把数据就绪之后,内核会将数据拷贝到⽤户线程,并返回结果给⽤户线程,⽤户线程才解除block状态。
⼆、⾮阻塞io我调⽤了⼀个磁盘io,但是我不⽤等他io完,我可以去进⾏别的东西,只要他io完就会通知我去读取数据;三、同步IO⾮阻塞io在进⾏磁盘io的时候,虽然不需要等磁盘io这个过程,但是当磁盘io完成之后,他还需要把数据从内核空间移动到⽤户空间,这个时间也是阻塞的,这就是同步.四、异步IO我调⽤了磁盘io,我会创建⼀个新的线程去处理这整个io,我只需要调⽤read,随后发⽣的什么时候,我创建了⼀个新的线程去帮我处理,等他处理完,我再回来接收已经准备好的数据.完全不⽤花费时间在等待,可以做别的事情。
这⾥粗略地举个栗⼦,⽐如你要玩英雄联盟。
阻塞IO就是,游戏登录进去之后你要先匹配队友(磁盘IO),匹配完队友后,你还要⾃⼰玩排位(把数据从内核复制到⽤户空间)。
⽽⾮阻塞IO就是你下课回到宿舍,让你室友先帮你排队(磁盘IO),你先去⼲点别的事情,⽐如上厕所,等你⼲完别的时候回来,发现游戏已经进⼊了,你可以直接选⽤英雄开始打了。
同步IO就是虽然你让你室友帮你匹配了队友,但是你还是得⾃⼰打游戏才能上分(把数据从内核复制到⽤户空间),这个时间段你也⽆法做别的事情。
⽽异步IO就是,你请⼀个代练,你只需要告诉他,我要上王者100点,那么他就会把所有事情都做得明明⽩⽩(磁盘io并且把数据从内核复制到⽤户空间),你拿到⼿的时候就已经是⼀个段位为王者的号了。
五、Socket1.⽂件标识符fd每⼀个程序都有⼀个基础流,⽂件标识符0、1、2,输⼊流、输出流、错误流。
如果这个进程监听了⼀个端⼝,那就会多⼀个3,是socket2.当服务端监听了某个端⼝只有监听了端⼝才会有3socket3.当客户端与服务端链接时服务端的⽂件标识符会变成4个,3是服务端监听了⼀个端⼝,就会创建这个socket,当客户端跟服务端链接的时候,就会多了⼀个socket,表⽰已经跟客户端建⽴了链接4.看源码nc localhost 8080 (建⽴连接的命令⾏)read() 读⽂件标识符accept() 当客户端想要跟你连接时,接收到⼀个fd,然后建⽴链接六、IO的内存模型时间⽚:当时间到了,调⽤进程调度,回调collBack,切换另⼀个进程所以进程越多,对CPU的性能压⼒越⼤切换的⽅式是利⽤晶振的规律震动,这⾥可以扩展到Redis的知识点,Redis是单线程, 采⽤单线程,避免了不必要的上下⽂切换.七、IO的发展1.阻塞模型如果fd4在read()阻塞了,此时如果有另外⼀个fd5,即使建⽴了连接,但是因为scoket在fd4阻塞住了.[缺点]:会发⽣阻塞2.抛出线程,阻塞在线程但是仍然有缺点,因为线程多了起来,线程的切换也需要消耗很⼤性能3.⾮阻塞I/O(NIO)只需要设置⼀下read,调⽤了read函数之后,不需要等待磁盘io,它会先返回⼀个结果,这样⼦不会阻塞,也不需要抛出线程.减少了线程的切换.但是仍然还有缺点:因为即使进程不需要等到磁盘io,但是磁盘读取完数据之后,我们还是需要把数据从内核空间复制到⽤户空间,这样⼦也会消耗性能.(因为read是对系统调⽤的⽅法)并且,如果有⼀万个客户端发来信息,但是我们每次去调⽤read的时候,是需要遍历1万个fd,才能发现哪个fd有事件(event),这是⼀个时间复杂度特别⾼的事情.⽽且⼀万个fd⾥⾯可能只有⼏个fd有事件,这就造成了浪费.4.多路复⽤在内核上进⾏优化,因为我们之前每次都是要把fd传到内核空间,然后再调⽤read判断它有没有事件,所以我们包装了⼀个select(),假设有⼀万个fd,我们⼀次性把fd传到内核⾥⾯,内核把这⼀万个fd遍历⼀遍,然后再把有事件的fd返回出去.在这⾥read 只需要调⽤有事件的fd次数,⽐如只有5个fd有事件发⽣,那么read只需要调⽤5次.但是仍然还有缺点,因为每⼀次调⽤read,都要在内核空间⾥⾯循环⼀万个fd,查看有没有事件(select),⽽且每次都要把fds这个集合复制到内核空间,这样⼦也会浪费资源.5.epoll所以我们在内核空间划出⼀块空间,把整个fds集合放在内核空间⾥⾯,这样⼦每此调⽤read的时候就不⽤涉及到内核态和⽤户态的切换了.客户端数据到达就会产⽣中断,然后查找⼀下这个终端号是那个fd的,然后返回给read就可以了⼋、NIO1.特点①、是基于流的形式,但⼜采⽤了缓冲区和管道来处理数据的;②、NIO是双向的;③、NIO是使⽤多路复⽤的IO模型.2.管道是什么?通道是对原 I/O 包中的流的模拟。
简述同步异步、阻塞与⾮阻塞欢迎指正1.关于1.1 涉及2个对象调⽤⽅(函数,对象,变量....) 与被调⽤⽅(函数)1.2 涉及概念:同步与异步、阻塞与⾮阻塞2.简述概念计算是需要时间的,OS处理函数所需的时间也不尽相同,有长有短。
下⾯有例⼦,but先说概念:2.1 同步:调⽤⽅调⽤函数后,必须等待函数的返回结果,才能继续执⾏下⼀次调⽤。
2.2 异步:调⽤⽅调⽤函数后,可以不⽤等待函数的结果,也可以执⾏下⼀次调⽤。
2.3 阻塞:线程的状态之⼀,通俗理解为:线程暂停运⾏,⼀直等待某个结果,⼀旦得到结果,线程继续执⾏。
这⾥说的是调⽤⽅所在线程。
2.4 ⾮阻塞:与2.3相反,调⽤⽅调⽤函数后,可以在被调⽤⽅(函数)运⾏期间做其他的事。
同步与异步:关注的是被调⽤⽅怎么把数据返回给调⽤⽅阻塞与⾮阻塞:关注的是线程的执⾏状态。
3.⼀个例⼦类⽐⽣活中拿快递:快递代收点与⽤户。
买到了⾃⼰⼼仪的宝贝, 当你收到代收点的通知,⾃⼰的宝贝已经到了,于是⾼⾼兴兴来到代收点:3.1 同步:此时,你对⼯作⼈员说:你好,我来取快递,取件码是XXX,谢谢。
⼯作⼈员:请稍等。
⼯作⼈员⽴即搜寻你的包裹,然后再确认信息(收件⼈名或者电话号码),再将包裹交给你。
3.2 异步:此时,你对⼯作员⼈说:你好,我来取快递,取件码是XXX,谢谢。
⼯作⼈员:好的,请在3mins后来取。
然后⼯作⼈员搜寻包裹或者处理其他事了,你在3min后再来拿包裹。
3.3 阻塞:此时,你对⼯作⼈员说明来意是取快递并告知取件码。
⼯组⼈员搜寻包裹的这段时间,你⼀直等待在这⾥,不能做其他的,眼巴巴的看着⼯作⼈员,真希望他能快点,等待⼯作⼈员将快递递给你。
3.4 ⾮阻塞:此时,你对⼯作⼈员说明来意是取快递并告知取件码。
⼯组⼈员搜寻包裹的这段时间,虽然你⼀直等待在这⾥,但是在等待⼯作⼈员将快递递给你的这段时间内,你可以做点其他的,打开淘宝,浏览宝贝。
4.组合接着上⾯的3的例⼦。
阻塞IO和⾮阻塞IO的区别
有很多⼈把阻塞认为是同步,把⾮阻塞认为是异步;个⼈认为这样是不准确的,当然从思想上可以这样类⽐,但⽅式是完全不同的,下⾯说说在JAVA⾥⾯阻塞IO和⾮阻塞IO的区别
在JDK1.4中引⼊了⼀个NIO的类库,使得Java涉及IO的操作拥有阻塞式和⾮阻塞式两种,问⼀下阻塞IO与⾮阻塞IO有什么区别?有什么优缺点?
在阻塞模式下,若从⽹络流中读取不到指定⼤⼩的数据量,阻塞IO就在那⾥阻塞着。
⽐如,已知后⾯会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就在那傻傻地等到下⼀个字节的到来,对,就在那等着,啥事也不做,直到把这10个字节读取完,这才将阻塞放开通⾏。
在⾮阻塞模式下,若从⽹络流中读取不到指定⼤⼩的数据量,⾮阻塞IO就⽴即通⾏。
⽐如,已知后⾯会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就读取这8个字节的数据,读完后就⽴即返回,等另外两个字节再来的时候再去读取。
从上⾯可以看出,阻塞IO在性能⽅⾯是很低下的,如果要使⽤阻塞IO完成⼀个Web服务器的话,那么对于每⼀个请求都必须启⽤⼀个线程进⾏处理。
⽽使⽤⾮阻塞IO的话,⼀到两个线程基本上就够了,因为线程不会产⽣阻塞,好⽐⼀下接收A请求的数据,另⼀下接收B请求的数据,等等,就是不停地东奔西跑,直接到把数据接收完了。
虽然说,⾮阻塞IO⽐阻塞IO有更⾼的性能,但是对于开发来的,难度就成数倍递增了。
由于是有多少数据就读取多少数据,这样在读取完整之前需要将已经读取到的数据保存起来,⽽且需要与其他地⽅来的数据隔离开来不能混在⼀起,否则就不知道这数据是谁的了。
同步/异步与阻塞/非阻塞的区别、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、1、同步阻塞I/O(最常用)I/O 密集型与CPU 密集型进程的比较I/O 密集型进程所执行的I/O 操作比执行的处理操作更多。
CPU 密集型的进程所执行的处理操作比I/O 操作更多。
Linux 2.6 的调度器实际上更加偏爱I/O 密集型的进程,因为它们通常会发起一个I/O 操作,然后进行阻塞,这就意味着其他工作都可以在两者之间有效地交错进行。
最常用的一个模型是同步阻塞I/O 模型。
在这个模型中,用户空间的应用程序执行一个系统调用,这会导致应用程序阻塞。
这意味着应用程序会一直阻塞,直到系统调用完成为止(数据传输完成或发生错误)。
调用应用程序处于一种不再消费CPU 而只是简单等待响应的状态,因此从处理的角度来看,这是非常有效的。
图2 给出了传统的模型,同步阻塞I/O模型,这也是目前应用程序中最为常用的一种模型。
其行为非常容易理解,其用法对于典型的应用程序来说都非常有效。
在调用 read 系统调用时,应用程序会阻塞并对内核进行上下文切换。
然后会触发读操作,当响应返回时(从我们正在从中读取的设备中返回),数据就被移动到用户空间的缓冲区中。
然后应用程序就会解除阻塞(read 调用返回)。
图 2. 同步阻塞I/O 模型的典型流程从应用程序的角度来说,read 调用会延续很长时间。
实际上,在内核执行读操作和其他工作时,应用程序的确会被阻塞。
2、同步非阻塞I/O(效率稍低)同步I/O 的一种效率稍低的变种是同步非阻塞I/O。
在这种模型中,设备是以非阻塞的形式打开的。
这意味着I/O 操作不会立即完成,read操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN 或 EWOULDBLOCK),如图3 所示。
图 3. 同步非阻塞I/O 模型的典型流程非阻塞的实现是I/O 命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成。
这可能效率不高,因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。
正如图 3 所示的一样,这个方法可以引入I/O 操作的延时,因为数据在内核中变为可用到用户调用 read 返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。
3、异步阻塞I/O(效率不是非常高)另外一个阻塞解决方案是带有阻塞通知的非阻塞I/O。
在这种模型中,配置的是非阻塞I/O,然后使用阻塞 select 系统调用来确定一个I/O 描述符何时有操作。
使 select 调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。
对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。
图 4. 异步阻塞I/O 模型的典型流程(select)select 调用的主要问题是它的效率不是非常高。
尽管这是异步通知使用的一种方便模型,但是对于高性能的I/O 操作来说不建议使用。
4、异步非阻塞I/O(AIO)最后,异步非阻塞I/O 模型是一种处理与I/O 重叠进行的模型。
读请求会立即返回,说明 read 请求已经成功发起了。
在后台完成读操作时,应用程序然后会执行其他处理操作。
当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次I/O 处理过程。
图 5. 异步非阻塞I/O 模型的典型流程在一个进程中为了执行多个I/O 请求而对计算操作和I/O 处理进行重叠处理的能力利用了处理速度与I/O 速度之间的差异。
当一个或多个I/O 请求挂起时,CPU 可以执行其他任务;或者更为常见的是,在发起其他I/O 的同时对已经完成的I/O 进行操作。
异步I/O 的动机从前面I/O 模型的分类中,我们可以看出AIO 的动机。
这种阻塞模型需要在I/O 操作开始时阻塞应用程序。
这意味着不可能同时重叠进行处理和I/O 操作。
同步非阻塞模型允许处理和I/O 操作重叠进行,但是这需要应用程序根据重现的规则来检查I/O 操作的状态。
这样就剩下异步非阻塞I/O 了,它允许处理和I/O 操作重叠进行,包括I/O 操作完成的通知。
除了需要阻塞之外,select 函数所提供的功能(异步阻塞I/O)与AIO 类似。
不过,它是对通知事件进行阻塞,而不是对I/O 调用进行阻塞。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、我喜欢用自己的语言通过联系现实生活中的一些现象解释一些概念,当我能做到这一点时,说明我已经理解了这个概念.今天要解释的概念是:同步/异步与阻塞/非阻塞的区别.这两组概念常常让人迷惑,因为它们都是涉及到IO处理,同时又有着一些相类似的地方.首先来解释同步和异步的概念,这两个概念与消息的通知机制有关.举个例子,比如我去银行办理业务,可能选择排队等候,也可能取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了.前者(排队等候)就是同步等待消息,而后者(等待别人通知)就是异步等待消息.在异步消息处理中,等待消息者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码)找到等待该事件的人.而在实际的程序中,同步消息处理就好比简单的read/write操作,它们需要等待这两个操作成功才能返回;而异步处理机制就是类似于select/poll之类的多路复用IO操作,当所关注的消息被触发时,由消息触发机制通知触发对消息的处理.其次再来解释一下阻塞和非阻塞,这两个概念与程序等待消息(无所谓同步或者异步)时的状态有关.继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行.相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,因为他(等待者)没有阻塞在这个消息通知上,而是zai一边做自己的事情一边等待.但是需要注意了,第一种同步非阻塞形式实际上是效率低下的,想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;而后者,异步非阻塞形式却没有这样的问题,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换.很多人会把同步和阻塞混淆,我想是因为很多时候同步操作会以阻塞的形式表现出来,比如很多人会写阻塞的read/write操作,但是别忘了可以对fd设置O_NONBLOCK标志位,这样就可以将同步操作变成非阻塞的了;同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞,比如如果用select函数,当select返回可读时再去read,一般都不会被阻塞,就好比当你的号码排到时,一般都是在你之前已经没有人了,所以你再去柜台办理业务就不会被阻塞.可见,同步/异步与阻塞/非阻塞是两组不同的概念,它们可以共存组合,也可以参见这里:/developerworks/cn/linux/l-async/-------------------分割线------------------------------昨晚写完这篇文章之后,今早来看了看反馈,同时再自己阅读了几遍,发现还是有一些地方解释的不够清楚,在这里继续补充完善一下我的说法,但愿没有越说越糊涂.同步和异步:上面提到过,同步和异步仅仅是关于所关注的消息如何通知的机制,而不是处理消息的机制.也就是说,同步的情况下,是由处理消息者自己去等待消息是否被触发,而异步的情况下是由触发机制来通知处理消息者,所以在异步机制中,处理消息者和触发机制之间就需要一个连接的桥梁.在我们举的例子中这个桥梁就是小纸条上面的号码,而在select/poll等IO多路复用机制中就是fd,当消息被触发时,触发机制通过fd找到处理该fd的处理函数.请注意理解消息通知和处理消息这两个概念,这是理解这个问题的关键所在.还是回到上面的例子,轮到你办理业务这个就是你关注的消息,而去办理业务就是对这个消息的处理,两者是有区别的.而在真实的IO操作时,所关注的消息就是该fd是否可读写,而对消息的处理就是对这个fd进行读写.同步/异步仅仅关注的是如何通知消息,它们对如何处理消息并不关心,好比说,银行的人仅仅通知你轮到你办理业务了,而如何办理业务他们是不知道的.而很多人之所以把同步和阻塞混淆,我想也是因为没有区分这两个概念,比如阻塞的read/write操作中,其实是把消息通知和处理消息结合在了一起,在这里所关注的消息就是fd是否可读/写,而处理消息则是对fd读/写.当我们将这个fd设置为非阻塞的时候,read/write操作就不会在等待消息通知这里阻塞,如果fd不可读/写则操作立即返回.很多人又会问了,异步操作不会是阻塞的吧?已经通知了有消息可以处理了就一定不是阻塞的了吧?其实异步操作是可以被阻塞住的,只不过通常不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.比如select函数,假如传入的最后一个timeout参数为NULL,那么如果所关注的事件没有一个被触发,程序就会一直阻塞在这个select调用处.而如果使用异步非阻塞的情况,比如aio_*组的操作,当我发起一个aio_read操作时,函数会马上返回不会被阻塞,当所关注的事件被触发时会调用之前注册的回调函数进行处理,具体可以参见我上面的连接给出的那篇文章.回到上面的例子中,如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;但是呢,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数),那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了.。