Golang线程模型
前言
Go的线程模型有核心三要素,也就是我们长说的GMP。 G(goroutine),M(内核线程),P(G的上下文),Go通过GMP实现了两极线程模型,今天的文章我们一起来探讨Go的线程模型是怎么样为Go带来了比较大的性能优势
两极线程模型
说道两极线程模型我们不得不聊下传统的三种线程模型了
一对一模型
如上图所示
一对一模型中的内核态线程和用户态线程一一对应
优点:
真正实现了并发
缺点: 用户态线程进行创建和销毁时最终会关联到内核态的线程创建和销毁,所以在频繁的导致在内核态和用户态之间进行切换
多对一模型
如上图所示
多对一模型中一个内核态线程对应了多个用户态线程。
优点: 避免频繁进行内核态和用户态的切换
缺点: 没有充分利用现在CPU多核的优势。举个例子: 当某一个用户态线程进行IO操作时,内核态线程需要进行syscall,那么其他的用户态线程就阻塞住了
多对多模型
多对多模型也叫两极线程模型(也就是Go的线程模型)。两极模型综合了上述模型。在两极模型中,一个内核态线程通过调度可能与多个用户态线程进行关联,同理一个用户态线程也可以关联多个内核态线程。
这种线程模型充分利用了多核CPU的特性,同时也避免了内核态到用户态的频繁切换
GMP
先奉上GMP样例图
m
m指向的是一个内核线程,它与内核线程一一对应. m的数据结构
type m struct {
// goroutine with scheduling stack
// g0协程在进程拉起时创建,执行Go程序的一些初始化操作
g0 *g
// 当前m正在运行的g的指针值
curg *g // current running goroutine
// 当前m正在关联的p
p puintptr // attached p for executing go code (nil if not executing go code)
// 预关联的p
nextp puintptr
// 前一个关联的p
oldp puintptr // the p that was attached before executing a syscall
// 当前m的标识
id int64
// 其他字段已省略
}
p
p提供g的上下文运行环境,当p与m建立关联之后,p中待运行的g有机会获得运行时机
p对应的状态值
// 当前p未与任何m进行关联,调度器会把把该状态的p加入空闲p列表
_Pidle = iota // 0
// 正在与m建立关联,可以执行用户代码
_Prunning //1
// p中运行的g正在执行系统调用
_Psyscall // 2
// Go runtime 要求停止调度(比如GC时)
_Pgcstop // 3
// 当前p不再被使用,等待GC回收
_Pdead //4
g
go程序创建的协程,可以用来保存协程运行时的一些堆栈信息
TODO 后续在完善
转载自:https://juejin.cn/post/7086809150021173278