GMP知识

单线程进程的缺点
  • 只有执行完一个任务才可以执行另外一个
  • cpu资源浪费,万一一个进程阻塞了(单进程没有切换能力)
多线程、进程的缺点
  • 切换成本 (cpu成本浪费,内存也是高消耗,一个进程虚拟内存接近4GB)
  • 开发设计变得复杂(同步竞争 )

调度器1:N模型

无法利用多个cpu,出现阻塞现象

1:N 有一个阻塞了,全都挂了,那我8核cpu,8个阻塞了,那就挂了?

老调度器
  • 创建销毁调度G,都需要每个M获得锁,造成了激烈的锁竞争

  • M转移G造成额外的系统负载(G里面创建G‘ 可能被其他线程执行)

  • CPU在M之间切换,导致频繁的线程阻塞和取消,增加了系统开销

    GMP模型概念简介

G goroutine 协程

P processor 处理器

M thread 线程

全局队列:存放等待运行的G

P的本地队列:数量限制,有限将新创建的G放到p的本地队列中,满了放到全局队列

P列表:程序启动时创建,GOMAXPROCS可以配置(环境变量)

M列表:当前操作系统分配到GO程序的内核线程数量

调度器的设计策略
  • 复用线程(尽量不进行创建或者销毁线程)

work stealing机制 (出现没有P队列的M时,分一个)

hand off机制(出现阻塞时,将队列和P寻找别的空闲或者休眠的M执行)

  • 利用并行

可以限定P的个数(来保证并行最大的数量)

  • 抢占

co-routine需要主动释放cpu不然就会阻塞,goroutine是一个抢占式模型(例如每个g最多等待10ms)

  • 全局G队列

如果其他p的本地队列也没有G了,就会从全局队列中获取(当然需要加锁和解锁)

go func经历了什么?

1、我们通过go func()来创建一个goroutine
2、有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先
保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;
3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行
4、一个M调度G执行的过程是一个循环机制(调度,执行,销毁,返回)
5、当M执行某一个G时候如果发生了系统调用或则其余阻塞操作,M会阻塞,如果当前有一些G在执行,runtime会把这个线程M从P中摘除(detach),然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P
6、当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态,加入到空闲线程中,然后这个G会被放入全局队列中。

执行go进程是会发什么什么?

M0: 启动程序后编号为0的主线程,在全局变量runtime.m0中,不需要再heap上进行分配。

负责初始化操作和第一个G,启动完成第一个G之后,M0就和其他M一样了。

G0:每次启动一个M,都会创建一个goroutinue,就是G0。G0是用来调度G的(M先切换到G0,使用G0来调度其他G)。M0的G0 会放再全局空间

场景
场景一

假如G1创建G3,优先创建到G1所在的本地队列中。(假如创建过多,不只有G3一个,并且G1所在的本地队列放不开,考虑将本地队列分成两半,前一半打散放到全局队列中,新创建的也放到全局队列中,后一半往前移)

场景二

优先执行本地队列的G

场景三

当创建一个G时,就会尝试唤醒一个thread

场景四

被唤醒的线程从全局队列批量获取G

场景五

全局队列中没有G,那么m就要进行work stealing(偷取),从其他G的P的队列中的偷取后半部分G过来,放到自己的P本地队列。

场景六

自旋线程+执行线程<=GoMAXPROCS

场景七

G发生系统调用时,或者阻塞时,M和P进行解绑,唤醒一个新的M与P进行绑定

场景八

G由阻塞转换为非阻塞时间,优先找到上一个分离的p,如果不是空闲就去全局的p队列中找,如果还是没找到p,G就会放到全局队列,M就会放到休眠队列等待调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值