关于同步异步非阻塞阻塞[推荐]
Posted zbuger
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于同步异步非阻塞阻塞[推荐]相关的知识,希望对你有一定的参考价值。
这里面有篇文章非常牛逼,说的很清楚(注意这句话System I/O can be blocking, or non-blocking synchronous, or non-blocking asynchronous)
http://www.artima.com/articles/io_design_patternsP.html(看这一篇足够了)
包括下面这篇
http://www.smithfox.com/?e=191
都说明了异步就是异步而已,异步下没有什么异步阻塞的概念
1. I/O就分三种,同步阻塞,同步非阻塞和非阻塞异步。可以这么理解,异步的话必然是非阻塞的了。
如第一篇文章所说blockingsynchronous, non-blocking snchronous, non-blocking asynchronous.
这里面所讲的概念其实都应该是函数调用,包括系统函数调用和用户层的函数调用,文章里面都是叫non-blocking asynchronous call怎么怎么招
所以,一个non-blocking asynchronous call就是call过去,然后做完了该做的事了后allback回来的机制而已,而这种机制必然需要一个函数调用来实现,也就是一个调用接口来实现,所以就有了异步调用同步调用之说了。
(这里先插一句Reactor和Proactor的区别,区别就是,
Reactor涉及同步IO(注意没说阻塞的事,当然肯定非阻塞效率高了,比如epoll反事件了要我调read去读,如果阻塞读,那么相当于只在epoll那等了一次事件,剩下的一直阻塞读了,肯定不高效。。),拿socket来讲,比如read,真正做read的是在哪里,Reactor模式就是说比如我调epoll,那么操作系统会反事件给我,但是是我用户层,也就是我caller方来调read,来做事,你被called的方只是给了我个事件,并没真正干活,这不叫异步。
而Proactor是涉及了异步IO(必然非阻塞),在这里就是真正的read是在OS里面做的,我只是传buffer给OS,OS会在kernel层把buffer填好,把活干完,然后callback我的handler,而我这个handler里面会处理完成了以后干什么,不会再涉及IO方面的read了,是相当于触发一个read请求,然后回调我了就是已经完成read了的事件了,所以windows上面叫IO complete ports.
The Reactor patterns involve synchronous I/O, whereas the Proactor pattern involves asynchronous I/O
Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备.
)
括号里面说的同样适用于两个程序的互相调用,接着上面说的 如果我调用一个non-blocking asynchronous call,那么应该是我call过去,然后做完了该做的事后callback回来caller方,调用方。 同理,假如对方也会反EAGAIN,然后也有事件通知,那就是把对方当做OS来想是一样的。
但是下面这篇文章说
同步和异步是针对应用程序和内核的交互而言的, 这并不全面,当然socket是这样的,但是如果是两个程序的接口之间,那么也是分同步异步的,就想上面说的,若果要实现那么复杂的话,也适用于两个程序的接口。不过似乎一般程序之间异步接口都是我call你之后就返回,你那边完事了回调了,就实现了异步调用。没有实现EAGAIN的机制。
2. epoll_wait这个系统调用本身,即可以使阻塞也可以使非阻塞,跟read一样。就看你把timeout设不设成零。所以第一篇文章里面称之为synchronous call。就是它即可是同步阻塞调用也可是同步非阻塞调用。
3.linux下socket方面目前没有真正的异步调用。所以linux只能实现Reactor模式。而只能用Reactor来模拟Proactor。就像第一篇文章里面提到的方法。WCG里面也是模拟的异步系统。UnixNetVConnection就是模拟的这层,在这里面把调read的活干了,写到用户空间(HttpSM)传入的buffer里面。然后callback回HttpSM。所以从HttSM的角度看,我do_io_read就是个异步接口,我调用接口,传入buffer,callback。然后我的callback被调就是read事件完成了。模拟了异步。Boost::asio是一样的。都是用Reactor模拟的Proactor。
4.其实讲服务器端,最好讲Reactor模式和Proactor模式。一般linux下都用epoll实现了Reactor模式,WCG算是极少的用epoll/poll模拟实现了Proactor模式的。跟人家谈这两种模式更专业
5.ACE实现了Reactor/Proactor模式,还有Half-Async/Half-sync模式等等。N多常见网络编程模式,有时间真得看看。
6.
Stevens在文章中一共比较了五种IO Model:
blocking IO
nonblocking IO
IO multiplexing
signal driven IO
asynchronous IO
在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。Stevens给出的定义(其实是POSIX的定义)是这样子的:
A synchronous I/O operation causes the requesting process to be blocked until that I/O operationcompletes;
An asynchronous I/O operation does not cause the requesting process to be blocked;
两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。有人可能会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。
(它这里面说进程被block应该是单线程程序。多线程的话,只是当前调recvfrom的会block直到拷贝完数据返回,别的线程可以继续干别的,很正常)
PS. windows下的overlapped IO就是所谓的asynchronous IO
下面是转载的一篇文章,主义前两段,非常提纲挈领。(主要看第一篇英文的,下面这篇还是有些概念不清,不过有些点比较好)
==================================================================================
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。
在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步,同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知)。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO
同步阻塞IO:
在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!
同步非阻塞IO:
在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
异步阻塞IO:(这个应该叫Reactor模式,或者说这里的异步指epoll的事件通知机制,并不准确,不存在这种说法应该)
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select属于同步操作。因为select之后,进程还需要读写数据),从而提高系统的并发性!
异步非阻塞IO:
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。
搞清楚了以上概念以后,我们再回过头来看看,Reactor模式和Proactor模式。
(其实阻塞与非阻塞都可以理解为同步范畴下才有的概念,对于异步,就不会再去分阻塞非阻塞。对于用户进程,接到异步通知后,就直接操作进程用户态空间里的数据好了。)
首先来看看Reactor模式,Reactor模式应用于同步I/O的场景。我们分别以读操作和写操作为例来看看Reactor中的具体步骤:
读取操作:
1. 应用程序注册读就绪事件和相关联的事件处理器
2. 事件分离器等待事件的发生
3. 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器
4. 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理
写入操作类似于读取操作,只不过第一步注册的是写就绪事件。
下面我们来看看Proactor模式中读取操作和写入操作的过程:
读取操作:
1. 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。
2. 事件分离器等待读取操作完成事件
3. 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作(异步IO都是操作系统负责将数据读写到应用传递进来的缓冲区供应用程序操作,操作系统扮演了重要角色),并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。
4. 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。
Proactor中写入操作和读取操作,只不过感兴趣的事件是写入完成事件。
从上面可以看出,Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备.
综上所述,同步和异步是相对于应用和内核的交互方式而言的,同步 需要主动去询问,而异步的时候内核在IO事件发生的时候通知应用程序,而阻塞和非阻塞仅仅是系统在调用系统调用的时候函数的实现方式而已。
//
说到阻塞,首先得说说I/O等待。I/O等待是不可避免的,那么既然有了等待,就会有阻塞,但是注意,我们说的阻塞是指当前发起I/O操作的进程被阻塞
同步阻塞I/O便是指,当进程调用某些涉及I/O操作的系统调用或库函数时,比如accept()(注意accept也算在了i/o操作)、send()、recv()等,进程便暂停下来,等待I/O操作完成再继续运行。这是一种简单而有
效的I/O模型,它可以和多进程结合起来有效的利用CPU资源,但是代价就是多进程的大量内存开销。
同步阻塞 进程坐水,就不能烧粥
同步非阻塞 类似于用一个进程坐水,烧粥. while(true)if... if... 好处就是一个进程处理多个i/o请求. 劣势就是需要不停的轮询.
区别在于等不等待数据就绪. 因为数据占了等待的80%时间. 同步非阻塞的优势就是一个进程里同时处理多个I/O操作。
在同步阻塞I/O中,进程实际上等待的时间可能包括两部分,一个是等待数据的就绪,另一个是等待数
据的复制,对于网络I/O来说,前者的时间可能要更长一些。
与此不同的是,同步非阻塞I/O的调用不会等待数据的就绪,如果数据不可读或者不可写,它会立即返
回告诉进程。
比如我们使用非阻塞recv()接收网络数据的时候,如果网卡缓冲区中没有可接收的数据,函数就及时返回,告诉进程没有数据可读了。相比于阻塞I/O,这种非阻塞I/O结合反复的轮询来尝试
数据是否就绪,防止进程被阻塞,最大的好处便在于可以在一个进程里同时处理多个I/O操作。但正是由于需要进程执行多次的轮询来查看数据是否就绪,这花费了大量的CPU时间,使得进程处于忙碌等待状态。
非阻塞I/O一般只针对网络I/O有效,我们只要在socket的选项设置中使用O_NONBLOCK即可,这样对于该socket的send()或recv()便采用非阻塞方式。
如果服务器想要同时接收多个TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,比如recv()。不管这些socket有没有可以接收的数据,都要询问一遍,假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket,这显然不是我们所希望看到的。
同步和异步,阻塞和非阻塞,有些混用,其实它们完全不是一回事,而且它们修饰的对象也不相同。
阻塞和非阻塞是指当进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时是直接返回还是等待就绪;
而同步和异步是指访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞(区别就绪与读写二个阶段,同步的读写必须阻塞),异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O,操作完毕的通知,这可以使进程在数据读写时也不阻塞。(等待"通知")
多数情况下,Web服务器对这些请求采用基于队列的自由竞争,通过多执行流(多进程或多线程)来充分占 用CPU以及I/O资源,减少任何无辜的等待时间,这其中包括了很多种具体实现的并发策略,
在实际应用中,特别是Web服务器,同时处理大量的文件描述符是必不可少的.多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程(比如电子屏,会闻到各个饭馆做好饭菜的味道)通过一种方法来同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。
回到买面条的故事中,假如你不止买了一份面条,还在其它几个小吃店买了饺子、粥、馅饼等,因为一起逛街的朋友看到你的面条后也饿了。这些东西都需要时间来等待制作。在同步非阻塞I/O模型中,你
要轮流不停的去各个小吃店询问进度,痛苦不堪。现在引入多路I/O就绪通知后,小吃城管理处给大厅安装了一块电子屏幕,以后所有小吃店的食物做好后,都会显示在屏幕上,这可真是个好消息,你只需
要间隔性的看看大屏幕就可以了,也许你还可以同时逛逛附近的商店,在不远处也可以看到大屏幕。
多路就绪:1.强调多路. 2.只针对请求数据是否就绪.不针对i/o读写
epoll针对的是这样的场景.
select, epoll都只需要进程(我)被动接收到数据就绪(面条)"通知".符合异步的定义. 不需要一直在饭馆等(同步阻塞).或轮询(同步非阻塞).
注:1.上面的内容主要来自《构建高性能Web站点》
2.对书中例子的理解: 我(进程) 超市小饭馆(socket) 面条(请求数据)
===============================================================================
boost asio用的是preactor模式。。。楼主不要误导人啊。reactor是指有IO事件到来的时候通知用户线程进行IO操作。而preactor模式是指用户线程提交IO操作,只要等待IO完成事件就可以了。LINUX下的EPOLL是reactor模式,windows下的iocp是preactor模式。
http://www.gamedev.net/topic/602713-boostasio-proactor-or-reactor-true-asyn-io/
Stevens在文章中一共比较了五种IO Model:
blocking IO
nonblocking IO
IO multiplexing
signal driven IO
asynchronous IO
http://blog.csdn.net/historyasamirror/article/details/5778378 讲的也不错,看看
以上是关于关于同步异步非阻塞阻塞[推荐]的主要内容,如果未能解决你的问题,请参考以下文章