深入讨论阻塞与非阻塞、同步与异步的区别

一、概念

异步:某个事情需要10s完成。而我只需要调用某个函数告诉xxx来帮我做(然后我再干其他的事情)

同步:某个事情需要10s完成,我需要一直等它完成(等10s),再能继续后面的工作。

阻塞:做某件事情,直到完成,除非超时

非阻塞:尝试做,如果不能做,就不做(直接返回),如果能做,就做。

前两者和后两者不容易区分,不过前两者更多的有涉及到多线程交互(消息)的场景。

二、举个例子

小李喝了想喝水,于是去煮开水。

1、小李把水壶放到炉子上,等待水烧开。(同步阻塞)

小李感觉这样太费时间。

2、小李把水壶放到炉子上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)

小李还是觉得自己这样太累,于是买了把会响笛的那种水壶。水开之后,能发出声音。

3、小李把响水壶放到炉子上,等待水壶发出声音。(异步阻塞)

觉得这样傻等意义不大

5、小李把响水壶放到炉子上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)

这样真好。

三、深入理解

阻塞就是 recv/read的时候 socket接收缓冲区要是有数据就读, 没数据我就一直睡觉赖着不走,直到有数据来了读完我才走。send/write的时候,要是发送缓冲区满了,没有空间继续发送了我也一直睡觉赖着不走,直到发送缓冲区腾出足够的空间让我把数据全部塞到发送缓冲区里我才走。(当然如果你通过setsockopt设置了读写超时,超时时间到了还是会返回-1和EAGAIN,不再睡觉等待)

非阻塞就是recv/read的时候,要是接收缓冲区有数据我就读完,没有数据我直接带着返回的-1和EGAIN走人,绝不睡觉等待耽误时间。write/send的时候, 要是发送缓冲区有足够的空间,就立刻把数据塞到发送缓冲区去,然后走人,如果发送缓存区满了,空间不足,那直接带着返回的-1和EAGAIN走人。

至于IO多路复用,首先要理解的是,操作系统为你提供了一个功能,当你的某个socket接收缓存区有数据可读,或者发送缓冲区有空间可写的时候,它可以给你一个通知。这样当配合非阻塞的socket使用时,只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。写操作类似。操作系统的这个功能通过select/poll/epoll之类的系统调用函数来使用,这些函数都可以同时监视多个描述符的读写就绪状况,这样,多个描述符的I/O操作都能在一个线程内完成,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程。

至于事件驱动,其实是I/O多路复用的一个另外的称呼。

上一篇:跟我学系列之JVM远程监控编写(一)


下一篇:Dubbo简化类图