Go的CSP并发模型实现M, P, G简介

GMP概念简介

G: goroutine(协程,也叫用户态线程)

M: 工作线程(内核态线程)

P: 上下文(也可以认为是cpu,逻辑cpu数量,可以在程序启动的时候设置这个数量,gomaxprocs函数设置)

GMP 模型

在 Go 中,线程是运行 goroutine 的实体,调度器的功能是把可运行的 goroutine 分配到工作线程上。

  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,不断重复下去。

有关 P 和 M 的个数问题

1.P 的数量:

由启动时环境变量 $GOMAXPROCS 或者是由 runtime 的方法 GOMAXPROCS() 决定。这意味着在程序执行的任意时刻都只有 $GOMAXPROCS 个 goroutine 在同时运行。
2. M 的数量:

(1) go 语言本身的限制:go 程序启动时,会设置 M 的最大数量,默认 10000. 但是内核很难支持这么多的线程数,所以这个限制可以忽略。

(2) runtime/debug 中的 SetMaxThreads 函数,设置 M 的最大数量

(3) 一个 M 阻塞了,会创建新的 M。

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 时候如果发生了 syscall 或则其余阻塞操作,M 会阻塞,如果当前有一些 G 在执行,runtime 会把这个线程 M 从 P 中摘除 (detach),然后再创建一个新的操作系统的线程 (如果有空闲的线程可用就复用空闲线程) 来服务于这个 P;

​ 6、当 M 系统调用结束时候,这个 G 会尝试获取一个空闲的 P 执行,并放入到这个 P 的本地队列。如果获取不到 P,那么这个线程 M 变成休眠状态, 加入到空闲线程中,然后这个 G 会被放入全局队列中。

相关推荐
Source.Liu18 分钟前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng18 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马21 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng24 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
Jacob程序员26 分钟前
java导出word文件(手绘)
java·开发语言·word
小白学大数据33 分钟前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
VBA633735 分钟前
VBA之Word应用第三章第三节:打开文档,并将文档分配给变量
开发语言
半盏茶香35 分钟前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏
落落鱼20131 小时前
tp接口 入口文件 500 错误原因
java·开发语言
hlsd#1 小时前
go 集成go-redis 缓存操作
redis·缓存·golang