[TOC]
### **什么是 GMP?**
> 先解释代表的意思:
G:Goroutine,实际上我们每次调用`go func`就是生成了一个 G。
P:Processor,处理器,一般 P 的数量就是处理器的核数,可以通过`GOMAXPROCS`进行修改。
M:Machine,系统线程。
> 再说GMP模型构成
在Go中,**线程是运行goroutine的实体,调度器的功能是把可运行的goroutine分配到工作线程上**
![](https://img.kancloud.cn/38/f8/38f83e7e7cffa0dfd2e22795cde4244b_1024x768.png)
1. **全局队列**(Global Queue):存放等待运行的G。
2. **P的本地队列**:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建G'时,G'优先加入到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列。
3. **P列表**:所有的P都在程序启动时创建,并保存在数组中,最多有`GOMAXPROCS`(可配置)个。
4. **M**:线程想运行任务就得获取P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列**拿**一批G放到P的本地队列,或从其他P的本地队列**偷**一半放到自己P的本地队列。M运行G,G执行之后,M会从P获取下一个G,不断重复下去。
> 最后说调度流程
![](https://img.kancloud.cn/f8/33/f833f08292f506ea308a4f8972aa0486_1920x1080.png)
1、 GPM 的调度流程从 go func()开始创建一个 goroutine,新建的G优先放入P的本地队列保存待执行的 goroutine(流程 2),当 M 绑定的 P 的的局部队列已经满了之后就会把 goroutine 放到全局队列(流 程 2-1)
2、每个 P 和一个 M 绑定,M 是真正的执行 P 中 goroutine 的实体(流程 3), M 从绑定的 P 中的局部队列获取 G 来执行
3、当 M 绑定的 P 的局部队列为空时,M 会从全局队列获取到本地队列来执行 G (流程 3.1),当从全局队列中没有获取到可执行的 G 时候,M 会从其他 P 的局部队列中偷取 G 来执行(流程 3.2),这种从其他 P 偷的方式称为 work stealing
4、一个M调度G执行的过程是一个循环机制
5、 当 G 因系统调用(syscall)阻塞时会阻塞 M,此时 P 会和 M 解绑即 hand off,并寻找新的空闲的 M,若没有空闲的 M 就会新建一个 M(流程 5.1)
6、当 G 因 channel 或者 network I/O 阻塞时,不会阻塞 M,M 会寻找其他的 G;当阻塞的 G 恢复后会重新进入 runnable 进入 P 队列等待执 行(流程 5.3)
7、 当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,则将G放入全局队列,等待被其他的P调度。然后M将进入缓存池睡眠。
### **抢占调度方式**
>协作式的抢占式调度
程序只能依靠 Goroutine 主动让出 CPU 资源才能触发调度,长时间占用线程,会造成其他Goroutine饥饿
>基于信号的抢占式调度(反应可能迟钝)
通过 sysmon 监控实现的抢占式调度,最快20us,最慢10-20ms
### **G-M-P的数量关系**
* M:有限制,默认数量限制是 10000,可调整。(debug.SetMaxThreads 设置)
* G:没限制,但受内存影响。
~~~
假设一个 Goroutine 创建需要 4k:
4k * 80,000 = 320,000k ≈ 0.3G内存
4k * 1,000,000 = 4,000,000k ≈ 4G内存
以此就可以相对计算出来一台单机在通俗情况下,所能够创建 Goroutine 的大概数量级别。
注:Goroutine 创建所需申请的 2-4k 是需要连续的内存块。
~~~
* P:受本机的核数影响,可大可小,不影响 G 的数量创建。(**`GOMAXPROCS`**)
### **GMP 调度过程中存在哪些阻塞**
* I/O,select
* block on syscall
* channel
* 等待锁
* runtime.Gosched()
### **Sysmon 有什么作用**
Sysmon 也叫监控线程,变动的周期性检查
* 释放闲置超过 5 分钟的 span 物理内存;
* 如果超过 2 分钟没有垃圾回收,强制执行;
* 将长时间未处理的 netpoll 添加到全局队列; 30
* 向长时间运行的 G 任务发出抢占调度(超过 10ms 的 g,会进行 retake);
* 收回因 syscall 长时间阻塞的 P;
- Go准备工作
- 依赖管理
- Go基础
- 1、变量和常量
- 2、基本数据类型
- 3、运算符
- 4、流程控制
- 5、数组
- 数组声明和初始化
- 遍历
- 数组是值类型
- 6、切片
- 定义
- slice其他内容
- 7、map
- 8、函数
- 函数基础
- 函数进阶
- 9、指针
- 10、结构体
- 类型别名和自定义类型
- 结构体
- 11、接口
- 12、反射
- 13、并发
- 14、网络编程
- 15、单元测试
- Go常用库/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go优化
- Go问题排查
- Go框架
- 基础知识点的思考
- 面试题
- 八股文
- 操作系统
- 整理一份资料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小点
- 树
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面试题
- 基础
- Map
- Chan
- GC
- GMP
- 并发
- 内存
- 算法
- docker