阻塞/非阻塞、同步/异步和Reactor/Proactor

目录

IO操作

阻塞/非阻塞、同步/异步

两种高效的事件处理模式

Reactor模式

 Proactor模式

 模拟 Proactor 模式


IO操作

一种说法:不需要cpu参与的都是IO操作,IO操作一般耗时比较长,所以任务大部分时间都在等待IO操作完成,所以当IO操作时,CPU都会异步去执行其他事情。

一种对IO操作新的解释:

        类unix世界中,不管socket还是FIFO、管道、终端,一切皆文件,而文件就是一串二进制流而已。在信息交换的过程中,我们都是对这些流进行数据的收发操作,简称IO操作

流中读出数据,系统调用read,写入数据,系统调用write。流通过文件描述符(fd)进行操作,一个fd就是一个整数,所以对这个整数的操作就是对这个文件(流)的操作。

总结:数据的收发就是IO操作,而IO操作相当费时

阻塞/非阻塞、同步/异步

        上面四个名词都是针对网络IO的解释,一个典型的IO有两个阶段:数据就绪(以何种方法监听数据)和数据读写(数据过来如何读和如何写)

        在数据就绪阶段就有阻塞和非阻塞;在数据读写阶段有同步和异步

        阻塞:在监听数据时,内核缓冲区没有数据时调用IO方法的线程或者进程挂起

        非阻塞:在监听数据时,通过IO函数返回值判断有无数据,无数据直接返回,不会改变进程或线程的状态。

        同步:当内核缓冲区有数据时,对该数据的读写操作由用户程序自己来完成

        异步:当内核缓冲区有数据时,对该数据的读写操作由内核来完成(一般非阻塞

        一个典型的网络IO接口调用,分为两个阶段,分别是“数据就绪” 和 “数据读写”,数据就绪阶段分为 阻塞和非阻塞,表现得结果就是,阻塞当前线程或是直接返回。

        同步表示A向B请求调用一个网络IO接口时(或者调用某个业务逻辑API接口时),数据的读写都是 由请求方A自己来完成的(不管是阻塞还是非阻塞);异步表示A向B请求调用一个网络IO接口时 (或者调用某个业务逻辑API接口时),向B传入请求的事件以及事件发生时通知的方式,A就可以 处理其它逻辑了,当B监听到事件处理完成后,会用事先约定好的通知方式,通知A处理结果。

注:1、阻塞时,进程或线程挂起不占用CPU,此时CPU忙其他的去了

        2、举例,非阻塞情况下利用recv函数,那么将根据返回值判断返回,具体如下:

                

        其中-1并不一定时出错了,还要判断是否是EINTR(被信号中断)、EAGAIN或EWOULDBLOCK(没有数据传入)

        3、数据就绪是在TCP的内核缓冲区,数据读写是在用户区,比如,同步就是自己将TCP的内核缓冲区数据读到自定义buf中,而异步是是内核帮我们进行数据的读

        4、异步有专门的IO函数,LINUX是AIO,Window是IOCP,.NET是BeginInvoke/EndlNVOke,操作系统在将数据读写完成后通常利用SIGIO信号进行通知。并且AIO也是模拟的异步,并且不支持网络传输

        5、只有同步才会有阻塞和非阻塞,异步一般是非阻塞的

        真正的异步IO需要CPU的深度参与。换句话说,只有用户线程在操作IO的时候根本不去考虑IO的执行全部都交给CPU去完成,而自己只等待一个完成信号的时候,才是真正的异步IO。所以,拉一个子线程去轮询、去死循环,或者使用select、poll、epool,都不是异步。

        6、异步一般需要读写数据的文件描述符,装数据的buf,通知方式,例如sigio信号

        7、线程同步:保证线程串行执行,和这里的同步异步不同


两种高效的事件处理模式

服务器程序通常处理三类事件:I/O事件、信号及定时事件

        两种高效的事件处理模式:Reactor和Proactor模式,还有一种很重要的模拟Proactor模式。其中Reactor模式使用同步IO实现,Proactor模式使用异步IO实现,模拟Proactor模式使用同步IO实现异步。

Reactor模式

        主线程(I/O处理单元)只监听文件描述符是否有事件发生,有的话就立即将事件通知工作线程,读写数据、接受新的连接、处理客户请求均在工作线程完成。

        使用同步I/O(epoll)实现Reactor模式的工作流程:

        1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。

        2. 主线程调用 epoll_wait 等待 socket 上有数据可读。

        3. 当 socket 上有数据可读时, epoll_wait 通知主线程。主线程则将 socket 可读事件放入请求队列。

        4. 睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后往 epoll 内核事件表中注册该 socket 上的写就绪事件。

        5. 当主线程调用 epoll_wait 等待 socket 可写。

        6. 当 socket 可写时,epoll_wait 通知主线程。主线程将 socket 可写事件放入请求队列。

        7. 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果。

 Proactor模式

        所有的IO操作均交给主线程和内核完成,工作线程仅负责业务逻辑。(编程复杂,并且linux下不支持网络socket)

        

 模拟 Proactor 模式

        使用同步 I/O 方式模拟出 Proactor 模式。主线程负责所有I/O操作,读写完成后主线程向工作线程通知这一“完成事件”。从工作线程角度,是直接获取到了数据读写结果,工作线程只负责业务逻辑。

        1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。

        2. 主线程调用 epoll_wait 等待 socket 上有数据可读。

        3. 当 socket 上有数据可读时,epoll_wait 通知主线程。主线程从 socket 循环读取数据,直到没有更 多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。

        4. 睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往 epoll 内核事 件表中注册 socket 上的写就绪事件。

        5. 主线程调用 epoll_wait 等待 socket 可写。

        6. 当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求的结果。

        同步 I/O 模拟 Proactor 模式的工作流程:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值