同步/异步/阻塞/非阻塞区别

转自于 http://www.cppblog.com/converse/archive/2009/05/13/82879.html 首先来解释同步和异步的概念,这两个概念与消息的通知机制有关.

举个例子,比如我去银行办理业务,可能选择排队等候,也可能取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了.
前者(排队等候)就是同步等待消息,而后者(等待别人通知)就是异步等待消息.在异步消息处理中,等待消息者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码)找到等待该事件的人.
而在实际的程序中,同步消息处理就好比简单的read/write操作,它们需要等待这两个操作成功才能返回;而异步处理机制就是类似于select/poll之类的多路复用IO操作,当所关注的消息被触发时,由消息触发机制通知触发对消息的处理.

其次再来解释一下阻塞和非阻塞,这两个概念与程序等待消息(无所谓同步或者异步)时的状态有关.
继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行.相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,因为他(等待者)没有阻塞在这个消息通知上,而是一边做自己的事情一边等待.但是需要注意了,第一种同步非阻塞形式实际上是效率低下的,想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;而后者,异步非阻塞形式却没有这样的问题,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换.

很多人会把同步和阻塞混淆,我想是因为很多时候同步操作会以阻塞的形式表现出来,比如很多人会写阻塞的read/write操作,但是别忘了可以对fd设置O_NONBLOCK标志位,这样就可以将同步操作变成非阻塞的了;同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞,比如如果用select函数,当select返回可读时再去read一般都不会被阻塞,就好比当你的号码排到时一般都是在你之前已经没有人了,所以你再去柜台办理业务就不会被阻塞.

可见,同步/异步与阻塞/非阻塞是两组不同的概念,它们可以共存组合,也可以参见这里:
http://www.ibm.com/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操作时,函数会马上返回不会被阻塞,当所关注的事件被触发时会调用之前注册的回调函数进行处理,具体可以参见我上面的连接给出的那篇文章.回到上面的例子中,如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;但是呢,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数),那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了.

同步和异步与阻塞与非阻塞是在通信和I/O中常用的字眼,之前在许多地方同步与阻塞,异步与非阻塞常常被混为一谈,带来了许多混乱,其实同步、异步和阻塞、非阻塞是两个不同的概念。最近随着异步IO(AIO)越来越多的应用,对这两个概念进行区分和解释的文章也越来越多,但是问起身边的同学,能说清楚的倒也不多,所以我就顺便跟风写一篇科普文吧(越来越水了=_=)。

同步(synchronous)和异步(asynchronous)其实是针对消息的发送和接受的次序而言的(在通信中就是消息的发送和接收,在IO中就是数据的读和写)。同步的意思就是消息的发送和接收是有序的,即接收和发送第二个包一定在第一个包之后第三个包之前,而不是乱序。异步的意思就是消息的发送和接收是可以乱序的,第一个包没发完可以直接发第二个包。

至于阻塞(block)和非阻塞(non-block)其实描述的是进程或线程进行等待时的一种方式。阻塞的意思是等待时进程或线程需要挂起,而非阻塞则是等待时线程或进程不需要被挂起,不影响线程的执行,这时线程或进程可以继续处理其它事物,不因为这个等待而受到影响(当然它仍然在等待这个消息,只不过可能会在线程或进程执行周期的某一个地方去查看消息的通知,而不是立即在原地等待)。

举个例子,两个人之间发短信,最简单的就是同步阻塞的方式,一个人发短信,然后啥也不干地等在手机前面,直到对方回信,接下来才发第二条短信(这时也确认了第一条短信已发到)。而同步非阻塞方式也就是大家常用的方式,则是发出去消息,然后去干别的事,(体现了非阻塞)等对方回短信之后(相当于确认了第一条短信已收到,并且有后续数据过来),再发第二条短信(体现了同步)。异步阻塞的方式,则是一口气发出几十条短信(由于中国移动并不保证发出短信的先后顺序,可能导致对方收到短信的顺序和发出去时不一致,这就体现了异步的概念,而且理论上发信的顺序也可以是乱的),发完之后就啥也不干,等对方一条一条的回信(这体现了阻塞的概念)。而如果在一口气发出几十条短信后没有傻傻的等待,而是去别的地方玩去了,对方的回信到一条读一条,则就变成异步非阻塞的方式了。

不知道通过上面的例子,大家是不是已经可以理解这两组概念之间的区别了。这里有篇相关的文章写得不错,如果还有些不理解的,可以再去阅读一下。由于国内在IT领域的起步落后国外(主要是美国)一些年份,再加上互联网的迅速普及,导致许多以讹传讹的现象时有发生。这两组本来适用范围并不相同的概念却在很长一段时间内被混为一谈,应该就是这方面的例子。这种错误增加了大家的学习成本,也不利于在某一些领域的进一步研究,所以个人以为搞清楚这些概念还是很有必要的(最后为自己的又一篇水文开脱一下=_=)

IO模型-- 同步和阻塞,异步和非阻塞的区别

这些词之间的区别难倒了很多人,还有什么同步阻塞, 同步非阻塞, 异步阻塞, 异步非阻塞,乱七八糟的。
很多文章也想讲明白这个问题。著名且引起热议的有
http://www.ibm.com/developerworks/cn/linux/l-async/
http://www.cppblog.com/converse/archive/2009/05/13/82879.html

可是看了之后还是有点将信将疑,跑到图书馆翻了UNP 第一卷,不愧是圣经级别的著作,似有所悟。
UNP所述:
POSIX定义中,同步IO操作:IO操作将导致请求进程阻塞,直到IO操作完成。
异步IO操作:IO操作不导致请求进程阻塞

UNP中总结的IO模型有5种之多
阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,前四种都属于同步IO。

阻塞IO不必说了
非阻塞IO ,IO请求时加上O_NONBLOCK一类的标志位,立刻返回,IO没有就绪会返回错误,需要请求进程主动轮询不断发IO请求直到返回正确
IO复用同非阻塞IO本质一样,不过利用了新的select系统调用,由内核来负责本来是请求进程该做的轮询操作。看似比非阻塞IO还多了一个系统调用开销,不过因为可以支持多路IO,才算提高了效率
信号驱动IO,调用sigaltion系统调用,当内核中IO数据就绪时以SIGIO信号通知请求进程,请求进程再把数据从内核读入到用户空间,这一步是阻塞的。
异步IO,如定义所说,不会因为IO操作阻塞,IO操作全部完成才通知请求进程。

这样以来,同步和阻塞,异步和非阻塞就不会被混淆了,它们不是同一个方面上的概念,不能比较区别

同步和异步是只跟IO操作过程中进程的状态变化有关
阻塞和非阻塞就是进程的两种状态。


你可能感兴趣的:(同步/异步/阻塞/非阻塞区别)