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 会被放入全局队列中。

相关推荐
心软小念33 分钟前
用Python requests库玩转接口自动化测试!测试工程师的实战秘籍
java·开发语言·python
sanggou2 小时前
【Python爬虫】手把手教你从零开始写爬虫,小白也能轻松学会!(附完整源码)
开发语言·爬虫·python
普通网友2 小时前
C++与Qt图形开发
开发语言·c++·算法
yue0082 小时前
C# 更改窗体样式
开发语言·c#
普通网友2 小时前
C++中的适配器模式
开发语言·c++·算法
风闲12172 小时前
Qt源码编译记录
开发语言·qt
普通网友3 小时前
C++中的委托构造函数
开发语言·c++·算法
月上柳青3 小时前
OpenWrt系统上配置batman-adv快速开始与配置详解
开发语言·mysql·php
全栈陈序员3 小时前
基于Rust 实现的豆瓣电影 Top250 爬虫项目
开发语言·爬虫·rust
普通网友3 小时前
C++中的代理模式实战
开发语言·c++·算法