下面都是一些最近学习IO的个人见解与总结,肯定会有很多不正确的理解与总结,还请多多指出。
线程的挂起与否,有内外两种因素:
- 外因:一般是资源竞争一个线程导致另一个或多个线程挂起。
- 内因:一般是IO操作导致的本线程挂起。阻塞 与 非阻塞 :
- 在于方法调用的结果是否立即返回,一定会导致线程的挂起与否。这是线程挂起的内因。同步 与 异步 :
- 在于 线程的执行是否依赖于某种顺序,为了保证某种的顺序,可能发生一个线程导致另一个或多个线程的挂起。这是线程挂起的外因。IO过程:
- 用户线程发起IO操作,(阶段一)用户线程等待数据,(阶段二)操作系统内核拷贝数据(从用户线程空间到内核空间或者相反)。IO类型:
针对 数据未准备就绪,是否返回一个标志结果。
- 阻塞IO:发起IO操作后,如果数据准备未就绪,不返回一个结果,直接IO阻塞,线程挂起。(用户线程两个阶段都会阻塞)
- 非阻塞IO:发起IO操作后,无论数据是否准备就绪,立即返回一个结果(用户线程只在第二个阶段被阻塞)。
针对用户线程于内核的交互,关键在于数据拷贝是否由用户线程主动调用内核操作完成(重点是线程在第二个阶段是否被阻塞)。
- 同步IO:用户线程发起IO操作,如果数据未准备就绪,用户线程或内核线程不断轮询数据状态直到数据准备就绪,再执行数据拷贝。(拷贝都是由用户线程来做的)
- 异步IO:用户线程只发起IO操作,数据的轮询和拷贝都是由操作系统内核自动来完成,完成之后,消息通知用户线程。
五种IO模型:
解释:下面括号内的分类中,同步和异步指的是多个同模型IO线程请求IO操作是否需要遵循某种顺序。而阻塞IO与非阻塞IO的定义与IO类型中的定义一样。
阻塞IO模型(同步阻塞IO):在读写过程中会发生阻塞现象。
非阻塞IO模型(同步非阻塞IO):在读写过程中的第二阶段会发生阻塞现象。
多路复用IO模型(异步阻塞IO):用一个线程来管理多个线程的IO操作,当某个线程的数据准备好之后,管理线程才真正调用内核IO执行数据拷贝,完成之后通知对应线程。
当然,在用户线程向管理线程注册IO操作的时候,也会被阻塞,直到数据拷贝完成。如果某一个IO操作完成则通知对应的线程。- 缺点:因为管理线程是顺序处理IO的,如果一个线程的响应体很大,后面的线程将会迟迟得不到响应。
- 优点:因为使用一个线程管理多个用户线程的IO,轮询的操作是由内核完成。故节省了资源占用,以及提高了程序效率。
信号驱动模型(异步非阻塞IO):用户线程请求IO操作,向操作系统注册一个信号函数,数据的轮询交给了内核,当数据准备就绪,通过信号函数来操作内核数据拷贝。此时用户线程依然被阻塞。
异步IO模型:用户线程发起IO操作之后,数据的轮询于拷贝都是内核自动完成,完成之后,通过消息通知用户线程。
两种高性能IO设计模式:
- Reactor 模式:对每个client注册感兴趣的事件,然后有一个线程去轮询每个client是否有感兴趣的事件到来,如果到来,则依次处理或则通过多线程或线程池来处理。
- 应用:多路复用IO;
- Proactor 模式:每当检测到一个请求发生,就新起一个异步操作,交给内核去处理,完成之后发送通知告之请求者任务已完成。
- 应用:异步IO;
- Reactor 模式:对每个client注册感兴趣的事件,然后有一个线程去轮询每个client是否有感兴趣的事件到来,如果到来,则依次处理或则通过多线程或线程池来处理。
总结:
- 同步IO通知的是数据准备就绪,异步IO通知的是数据操作完成。
- 前四种IO模型都是同步IO类型。只有最后一种模型才是最理想的IO模型。不过需要内核的支持。
- IO的阻塞与否在于请求IO操作是否立即有结果返回。IO的同步异步在于,IO操作的两个过程是否同时需要用户线程被挂起(内核拷贝线程与用户线程运行的顺序)。