阻塞与非阻塞,同步与异步
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。
普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
所谓阻塞非阻塞,仅仅对于老张而言。
立等的老张,阻塞;看电视的老张,非阻塞。
情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
程序员写程序,很多情况下,老张和水壶都要涉及,即调用者与被调用者。四种组合模式各有所长。
当到银行后,
.可以去ATM机前排队等候 -- (排队等候)就是同步等待消息
.可以去大厅拿号,等到排到我的号时,
柜台的人会通知我轮到我去办理业务. -- (等待别人通知)就是异步等待消息.
A. 阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
socket接收数据函数recv是一个阻塞调用的例子。
当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
B. 非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
同步阻塞形式:
效率是最低的,
拿上面的例子来说,就是你专心排队,什么别的事都不做。
实际程序中
就是未对fd 设置O_NONBLOCK 标志位的read/write 操作,
异步阻塞形式:
如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,
假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;
异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.
比如select 函数,
假如传入的最后一个timeout 参数为NULL,那么如果所关注的事件没有一个被触发,
程序就会一直阻塞在这个select 调用处.
同步非阻塞形式:
实际上是效率低下的,
想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,
如果把打电话和观察排队的位置看成是程序的两个操作的话,
这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;
很多人会写阻塞的read/write 操作,
但是别忘了可以对fd 设置O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了;
异步非阻塞形式:
效率更高,
因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,
程序没有在两种不同的操作中来回切换.
比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,
于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数),
那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了.
如果使用异步非阻塞的情况,
比如aio_*组的操作,当发起一个aio_read 操作时,函数会马上返回不会被阻塞,
当所关注的事件被触发时会调用之前注册的回调函数进行处理,