什么是IO多路复用?就是异步阻塞IO。目的在于提升系统效率。
服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:
(1)同步阻塞IO(Blocking IO):即传统的IO模型。
(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。
(3)IO多路复用(IO Multiplexing):即经典的Reactor模式(并非23种设计模式之一),有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
Reactor模式称为反应器模式或应答者模式,是基于事件驱动的设计模式,拥有一个或多个并发输入源,有一个服务处理器和多个请求处理器,服务处理器会同步的将输入的请求事件以多路复用的方式分发给相应的请求处理器。 Reactor设计模式是一种为处理并发服务请求,并将请求提交到一个或多个服务处理程序的事件设计模式。(感觉像外观模式facade)
(4)异步IO(Asynchronous IO):即经典的Proactor模式(前摄器模式),也称为异步非阻塞IO。
Reactor模式是一种被动的处理,即有事件发生时被动处理。而Proator模式则是主动发起异步调用,然后循环检测完成事件。
一、同步和异步
同步和异步的概念描述的是用户线程与内核的交互方式,主体是线程:
同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;
而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
同步异步的主体是用户线程。
二、阻塞和非阻塞
阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式,主体是IO:
阻塞是指IO操作需要彻底完成后才返回到用户空间;
而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。
阻塞非阻塞的主体是IO。
看图比较容易明白。
所以,有道是:线程同步,IO阻塞。
三、同步阻塞IO
用户需要等待read将socket中的数据读取到buffer后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够。
四、同步非阻塞IO
用户线程发起IO请求后,立即返回;但需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。
虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
五、异步阻塞IO(IO多路复用)
异步阻塞模型使用内核提供的select函数(多路分离函数),避免同步非阻塞模型中轮询等待的问题。
表面上看,异步阻塞模型和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,它最大的优势是可以在一个线程内同时处理多个IO请求:用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的,所以又叫IO多路复用模型。
虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。以下是改良版:
六、异步非阻塞IO(异步IO)
“真正”的异步IO需要操作系统更强的支持。在IO多路复用模型中,用户线程收到通知后,自行读取数据、处理数据。而在异步IO模型中,则由内核读取数据,并放在缓冲区,用户线程收到通知后,直接使用即可。
七、异步阻塞IO为什么叫IO多路复用
原因上面已经说了,IO多路复用其实就是异步阻塞IO,就是一个线程,可以同时处理多个IO请求(反过来不成立,一个IO请求只能对应一个线程)。
其实“I/O多路复用”这个说法之所以难以理解,可能是翻译比较坑爹。所谓的I/O多路复用在英文中其实叫 I/O multiplexing。这里面的 multiplexing 指的是通过跟踪每一个Socket的状态来同时管理多个I/O流。其目的,在于尽量多的提高服务器的吞吐能力。
八、同步异步、阻塞非阻塞为什么和多线程、IO联系在一起?
线程为什么会和IO扯在一起?
一个线程的执行,通常需要 3 个资源,即CPU,内存和I/O。CPU负责运行,内存负责存放即时数据,I/O负责和磁盘、数据库、网络等做数据交换。
I/O,即输入/输出。Java中,常见的I/O,有文件流,数据库连接,网络连接等。
I/O的特点是每个连接单位时间内只能为一个线程服务,它不像CPU,即使是单核CPU,也可以通过时间切片的方式,执行多线程,多核CPU就更不用说了。IO只能为单一线程服务。还有一个特点是,当一个线程在使用I/O时,一般耗时相对较长,且在这个时间段内,线程不进行计算或读写内存,换言之,线程在使用I/O时,一般不会使用CPU或内存。这个特点很重要,这其实是多线程的重要意义之一。在一个线程使用I/O时,将CPU使用权转移给其它线程,可以更加充分的利用系统资源。
参考文章:
IO多路复用机制详解
[设计模式]Proactor前摄器模式和Reactor反应器模式
2022.08.25
IO多路复用与是否阻塞应该没有关系。IO多路复用是指,一个线程可以同时发出多个IO请求,监听多个socket。