在《轻松搞懂5种IO模型》中,我发起了一个投票。
答案是【同步IO多路复用】。目前,60%的朋友答对了。原因这里解释一下。
同步和异步的概念区别
同步:线程自己去获取结果。(一个线程)
异步:线程自己不去获取结果,而由其他线程送结果。(至少两个线程)
异步执行如下图所示,除非不需要知道结果,否则一般会有一个回调方法。
IO多路复用的本质
为了彻底理解IO多路复用是同步还是异步,咱们探究一下IO多路复用的本质。
I/O多路复用,复用的IO监听等待这条路。实际上就是用select/poll/epoll监听多个io对象,当io对象有变化(有数据)的时候就通知用户进程。好处就是单个进程可以处理多个socket。
select/poll/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
对于每一个socket,一般都设置成为non-blocking,但是,整个用户的process其实是一直被阻塞的。只不过process是被select这个函数阻塞,而不是被socket IO给阻塞。
I/O多路复用的流程如上图所示:
(1)当用户进程调用了select,那么整个进程会被阻塞;
(2)而同时,内核会“监视”所有select负责的socket;
(3)当任何一个socket中的数据准备好了,select就会返回;
(4)这个时候用户进程再调用read/accept/write操作,做一些数据从内核拷贝到用户进程这样的事情。
所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
事实上,I/O 多路复用有时候性能比同步阻塞IO还更差一些。因为这里需要使用两个系统调用(select 和 recvfrom),而同步阻塞IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个连接。所以,如果处理的连接数不是很高的话,可能延迟还更大。
总结
打个比方:行军打仗讲究粮草先行。诸葛亮比较牛,他打仗只带少量粮草,其他靠敌军送。这天他又派了暗探去查看敌军粮草的守卫情况。如果敌人守备松懈,则趁机偷粮。如果这个暗探只偷一袋粮食,那效率最高的是不是他看到敌军守备松懈就直接进去偷粮(同步阻塞IO)?但是他要偷的是十万大军的粮食,那他就要先回去汇报一声:“守备松懈啦”。然后百人小分队一起去把粮草偷出来(I/O 多路复用)。当然啦,以诸葛亮的一贯作风而言,最后他还得放一把火。
暗探在同步阻塞模式下,打探敌情也是他,偷粮也是他。在诸葛亮团队中,暗探在打探敌情时最终暗探是第一个获取到结果的。暗探在偷粮时也是第一个自己知道结果的。(同步)
暗探在I/O 多路复用模式下,打探敌情也是他,偷粮是百人小分队。在诸葛亮团队中,暗探在打探敌情时最终执行者暗探是第一个获取到结果的。百人小分队在偷粮时也是百人小分队自己先知道结果的。(同步)
综上,IO多路复用是同步的。