总结一下 linux 中的线程模型
概述
linux 最开始使用的线程是linuxThreads
, 但是linuxThreads
不符合POSIX
标准, 后来出现了NGPT
, 性能更高, 之后又出现了NPTL
, 比NGPT
更快, 随着时间推移, 就只剩下NPTL
了
NPTL
>NGPT
>linuxThreads
线程的模型分为三种:
- 多对一(M:1)的用户级线程模型
- 一对一(1:1)的内核级线程模型: 例如
linuxThreads
和NPTL
- 多对多(M:N)的两极线程模型: 例如
NGPT
多对一
多对一很好理解, 在多对一中, 线程的创建/调度/同步的细节都由进程的用户空间线程库来处理, 也就是说由语言自己来处理.
用户态线程的很多操作对内核来讲, 都是透明的, 不需要内核来进行接管, 就意味着不需要进行内核态和用户态进行频繁的切换. 导致线程的创建/调度/同步的处理速度非常的快.
当然也有缺点, 当多线程并发执行时, 其中一个线程执行 IO 操作需要经过内核时, 内核接管了操作出现阻塞, 就导致了用户态的其他线程都会被阻塞. 因为这些线程实际上都是一个内核调度处理. 如果语言不对多核进行优化时, 就会导致只有一个内核处理线程, 也就无法调度到其他的核心, 就变成了伪线程
一对一
在一对一模型中, 每个用户线程都对应各自的内核调度实体, 内核对每个线程进行调度, 在阻塞时将可以其他线程调度到其他核心处理.
线程每次操作都会在用户态和内核态直接进行切换, 消耗一定的资源, 同时, 内核为每个线程都配置一个核心, 导致线程如果很多时, 会影响性能
在现代硬件上, CPU 一般都是多核, 因此相比多对一来讲, 一对一能更好的使用资源
多对多
多对多综合力一对一和多对一的优点, 每个线程可以拥有多个调度实体, 也可以多线程对应一个调度实体
但是多对多的调度需要内核态和用户态一起联动实现, 导致该模型非常复杂
go
go 的 GMP 模型, 属于是多对多模型, 默认情况下, 真正的核心实体数量为 CPU 的内核数量, go 的 goroutine 是 go 内部的线程, 对应 GMP 中的 G, GMP 中的 P 会对 G 进行调度, 落到真正的核心实体, 也就是 GMP 中的 M 进行运行
更多的 go 调度部分逻辑, 查看我的其他相关文章