IO线程模型一直在演化,由最开始的单线程模型,到BIO方式的单线程接受请求线程池线程具体处理单个请求的读写事件,再到NIO的单线程接受请求线程池里面的单个线程可以处理不同请求的读写事件,一个字没有最快,只有更快。
引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。
进程中线程的共享数据与私有数据
共享:代码段,数据段,文件描述符,信号。
私有:线程ID,寄存器,栈,程序计数器,errno。
常见的多线程编程模式
1. Leader-Follow 模型(主从)
线程与连接对应,并发度等于线程数。
所有线程经历accept->close整个过程。
适用于连接数少、处理时间长、CPU密集型服务。
上图就是L/F多线程模型的状态变迁图,共6个关键点:
1) 线程有3种状态:领导leading,处理processing,追随following
2) 假设共N个线程,其中只有1个leading线程(等待任务),x个processing线程(处理),余下有N-1-x个following线程(空闲)
3) 有一把锁,谁抢到就是leading
4) 事件/任务来到时,leading线程会对其进行处理,从而转化为processing状态,处理完成之后,又转变为following
5) 丢失leading后,following会尝试抢锁,抢到则变为leading,否则保持following
6) following不干事,就是抢锁,力图成为leading。
Nginx的做法,leader只负责与外界通信,而follower去干活。只需用一个Mutex对临界区进行互斥,获取锁的自动提升为Leader,没有抢到的继续当Follower吧。不过这种设计思路有惊群效应, Linux早就从内核层解决了这个问题。
2. producer-consumer模型(生产者消费者)
1)主线程用于accept请求,并将fd放置在消费队列pendingpool中。
2)pendingpool进行连接的维护工作。
3)多个worker竞争pendingpool的连接。
4)适用于连接数多、处理速度快的业务。
生产者消费者线程模型常常用于网络编程,即一个线程监听事件发生(生产者,例如产生“收到数据”事件),其他线程来处理事件(消费者,例如处理收到的数据)
3. 高并发索引模型
无锁设计
将请求或者事务映射到具体线程处理