主页 > 手机  > 

go学习-GMP模型

go学习-GMP模型
GMP 好理解还是 GPM 好理解?

按照上述图,从上往下,GPM更适合理解 GMP 模型: Go 语言运行时系统中的 Goroutine、用于管理 Goroutine 调度的 Go Scheduler(P)、机器可用的逻辑处理器数量(M)。

理解GPM G

每个 Goroutine 是一个轻量级“线程”,称之为“协程”,可由 Go 运行时系统并发执行

G与P的关系

Goroutine 通过 Go Scheduler 调度运行 Go Scheduler 负责在适当时间调度 Goroutine 的执行 并在必要时将 Goroutine 阻塞和调度到其他 Goroutine 执行上下文

P

P 是中介者,连接 Goroutine 和 M,由于 Go Scheduler 在用户 CPU 时间片内并行执行 Goroutine,因此也必须用 P 进行有效的调度。 Go 运行时系统可以自动调整 P 的数量,以确保它们与当前运行的 Goroutine 的数量大致相同,并且可以在需要更多/更少 P 时及时适应。

P 叫 go运行时的逻辑并发单元,每个OS线程需要获取一个P才能执行 每个P中都包含运行一个线程所需的全部资源(比如线程栈),包括存放G的就绪队列,以及分配堆内存所需的地址空间等 这些在运行main.main前就在runtime.main中(程序启动时)创建好,所以并发执行时就可以避免冲突

P的本地队列上限默认最多 GOMAXPROCS = 256 个,如果想要更改队列长度限制,使用 runtime.GOMAXPROCS(n) 调整

M

M 是处理器,绑定一个具体的OS线程(内核线程),Goroutine 运行的上下文在这里被调度并执行,所以也是 Goroutine 执行的上下文 管理 Goroutine 的运行状态、切换执行上下文 Go 运行时系统会根据机器硬件和条件动态地分配这些处理器,以确保 Goroutine 能够高效地并发运行

一个 M 最多有一个空闲的 P,有一个M阻塞,会创建或者唤醒一个新的M;若有空闲,则会回收或睡眠此M。 全局队列作为一个候选队列,可以同时为不同的 P 提供 Goroutine,从而使 Goroutine 尽快找到执行线程(M) 如果所有的 P 的本地队列都已满,并且全局队列也已满,Go 运行时系统将通过动态增加 M 的数量来解决压力 以尽快地使 Goroutine 开始运行,但OS是否愿意加M,则需要根据系统负载来定。

在默认情况下,全局队列的大小是默认 P 的数量的系数,最小值为 4,最大值为 32768

OS分配到当前Go程序的内核线程数

使用 runtime.NumCPU() 查询M数量,返回当前系统可用的逻辑处理器数量

返回值可能会因为不同的操作系统和系统配置而有所不同 有些操作系统可能会在系统负载高峰期动态增加或减少 M 的数量,以适应不同的负载需求 它只会返回运行时系统认为当前可用的逻辑处理器数量,而不是一成不变的固定值

当所有 P 的本地队列都已满,且全局队列也满时,有新的Goroutine就绪 Go 运行时系统会请求操作系统分配更多线程,以进一步提高系统并发处理的能力,启动一个新线程,即新的 M (machine)新的 M 将从全局队列中拿走一个等待 Goroutine 并开始运行它新的 M 将在全局队列和所有本地队列之间轮询,直到所有队列都变得空闲或有 Goroutine 准备好运行,这将减少因 Goroutine 等待执行带来的延迟 GMP 特点

Go 语言的并发性能和可伸缩性得到了最大化的利用。 当 CPU 资源充足时,它可以在 Goroutine 和 P 之间平衡负载,并在适当的时候调度 Goroutine 到合适的 M 上以保证运行效率

Go 协程调度为什么优于线程 多进程/多线程问题

进程/线程数量越多,切换成本越大 多进程/多线程的壁垒:高内存占用,高CPU调度切换 同步竞争,如锁等

协程如何设计

N:1 : 无法利用多个CPU,出现阻塞瓶颈 ​1:1 :和多线程、多进程无区别,切换协程成本代价昂贵 ​M:N :能够利用多核,过度依赖协程调度器的优化和算法

如何高效 复用线程

避免频繁的创建、销毁线程,而是对线程的复用

Work stealing机制 (work偷窃)

当本线程的P队列中没有G可用并且全局队列为空时,会主动去其他线程的P队列中获取,避免线程的销毁

handle off 机制

当本线程的G运行阻塞时,会释放绑定的P队列,把P队列转移到其他空闲的线程上去

利用并行

GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在CPU上运行

抢占

一个Goroutine 最多占用CPU 10ms 就会交给其他协程,防止其被饿死 Go 语言的抢占是基于协作式调度的,而切换的主动权在 Goroutine 执行上

通过代码的设计来主动让 Goroutine 放弃控制权,以便让其它 Goroutine 获得执行时间片 使用 runtime.Gosched() 主动让当前 Goroutine 放弃 CPU 控制权,使当前 Goroutine 立即进入就绪状态,并重新排队等待下一次调度内部编写超时限制的代码来防止 Goroutine 长时间执行

Go如何调整协程优先级? 在 Go 语言中,所有 Goroutine 都有相同的默认优先级,不能直接通过代码来调整 Goroutine 的优先级 这个是不是一个协程的缺点?我觉着是

全局G队列

当M执行work stealing 机制时,无法从其他P中获取G,就会从全局队列中获取

总结

G是买家,M是卖家,M根据最近的行情,动态调整,也可以去抢其他M的活 G呢,买的东西也不知道是哪个M操办的,G之间是公平的

参考文档

juejin /post/7119307307728994335

标签:

go学习-GMP模型由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“go学习-GMP模型