Go 并发编程 - runtime 协程调度(三)
Go Runtime
Go runtime 可以形象的理解为 Go 程序运行时的环境,类似于 JVM。不同于 JVM 的是,Go 的 runtime 与业务程序直接打包在一块,是一个可执行文件,直接运行在操作系统上,效率很高。
runtime 包含了一些 Go 的一些非常核心的功能:协程调度、垃圾回收、内存分配等。本文将着重介绍协程调度(GMP 模型)。
协程调度
协程调度是指 Go 如何管理和执行协程,Go 的协程调度基于 GMP 模型。即:
- G (Goroutine):即 Go 的协程,包含了栈信息,代码指针,状态等;
- M (Machine):代表一个工作线程,由操作系统直接分配;
- P (Processor):处理器(Go 定义的一个概念,不是指 CPU),包含了协程运行的所需资源,如本地队列、全局队列、计数器等。
GMP 三者的关系:
- P 的个数取决于设置的 runtime.GOMAXPROCS,默认是物理 CPU 的逻辑核心数量,比如四核八线程的 CPU,P 的数量就是 8;
- M 的数量一般是多于 P 的,M 要想被 CPU 执行,必须先获取 P。没有获取 P 的 M,则处于休眠状态;
- G 可以理解为代码本体,G 必须要被 P 调度进入 M,才可以被 CPU 执行;
- P 包含了一个 LRQ (Local Run Queue)本地运行队列,保存着等待执行的协程(G)队列。没有被分配到 P 的 G,会被保存到 GRQ (Global Run Queue) 全局队列中,处于休眠状态。
假如主机是单逻辑 CPU 的,那么 GMP 是这样的:
红色部分表示休眠或者挂起状态,黄色代表等待执行,绿色表示正在运行。系统初始化了两个线程,但我们只有一个处理器(P), M1 没有获取到 P,所以只能休眠。M0 当前获取到 P ,正在处理 G0, LRQ 里面目前有三个 G 在排队等待被 M 运行,GRQ 里面保存着 G4、G5、G6,表示它们还没有分配到队列中。
P 这个时候会分别对 LRQ 进行周期队列轮转 和 GRO 周期性检查:
- 队列轮转:LRQ 中的 G 被 P 调度到 M 中执行,每个 G 执行一段时间后,就会保存其上下文并放入队列尾部,然后取出队列头部的 G 进入 M 执行。
- 周期性检查:P 会检查 GRQ 中是否有 G 正在等待运行,并将其放入 M 中执行,防止协程被饿死。
- 在队列轮转中,如果当前正在运行的 G 遇到了系统调用,那么系统就会挂起当前 M0,释放 P,M1 就会绑定释放的 P,来继续执行其他协程。
假设 G0 遇到了系统调用:
等到 M1 中所有的协程执行完或者 M1 处理某个协程也遇到了了系统调用,就会重新释放 P 给其他空闲的 M。而另外一边 G0 的系统调用结束后,就会将 M0 线程从挂起状态变成休眠状态,并将 G0 放入 GRQ,等待被 P 重新调入 LRQ 中轮转执行。
如果我们的主机具备多个逻辑 CPU,创建了多个 P,那么就会变成多个线程并行执行:
多线程同时处理时,很有可能多个 LRQ 是不均衡的。假如上图的 M0 已经执行完了,其他线程还处于繁忙状态,M0 所绑定的 P 就会去检查 GQR,GQR 中也没有 G,那么它就会去偷取其他 LRQ 一部分的 G 来执行,一般每次会偷取一半。
runtime 包
runtime.GOMAXPROCS
runtime.GOMAXPROCS()
可以用来设置 P 的数量,一般设置为和逻辑 CPU 数量相等的值:
fmt.Println(runtime.NumCPU())
runtime.GOMAXPROCS(runtime.NumCPU()) // 使用所有的逻辑 CPU
// 结果
我的主机 CPU 是16核24线程,所以会使用24个 P
runtime.Gosched
runtime.Gosched()
用于让出当前协程的运行时间片,也就是当 P 遇到它时,会先安排其他协程先执行:
func main() {
runtime.GOMAXPROCS(1)
go func() {
for i := 0; i < 5; i++ {
fmt.Println("go")
}
}()
runtime.Gosched()
fmt.Println("hello")
}
// 结果
输入结果不是固定的,有可能是
go
go
go
go
go
hello
也有可能是
go
go
go
go
hello
go
也有可能是
hello
输出第一种情况容易理解,主协程让出了时间片,理所应当先打印 Go,但是如果子协程还没有来得及被调度或者打印,就会出现其他情况。
runtime.Goexit
runtime.Goexit() 会结束当前的协程,但是 defer 语句会正常执行。此语法不能在主函数中使用,会引发 panic:
func main() {
runtime.GOMAXPROCS(1)
go func() {
defer fmt.Println("defer不受影响")
fmt.Println("我被执行了")
runtime.Goexit()
fmt.Println("我被跳过了")
}()
time.Sleep(1 * time.Second)
}
// 结果
我被执行了
defer不受影响
本系列文章:
Go 并发编程 - runtime 协程调度(三)的更多相关文章
- 四 python并发编程之协程
一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...
- 37、python并发编程之协程
目录: 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本节的主题是基于单线程来 ...
- python并发编程之协程知识点
由线程遗留下的问题:GIL导致多个线程不能真正的并行,CPython中多个线程不能并行 单线程实现并发:切换+保存状态 第一种方法:使用yield,yield可以保存状态.yield的状态保存与操作系 ...
- python全栈开发从入门到放弃之socket并发编程之协程
一.为什么会有协程 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情 ...
- 32 python 并发编程之协程
一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...
- 百万年薪python之路 -- 并发编程之 协程
协程 一. 协程的引入 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两 ...
- python并发编程之协程(实践篇)
一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 对于单线程下,我们不可避免程序中出现io操作,但如果我们 ...
- 第十篇.5、python并发编程之协程
一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...
- 第 12 章 python并发编程之协程
一.引子 主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只用一个)情况下实现并发,并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作 ...
- 网络编程进阶:并发编程之协程、IO模型
协程: 基于单线程实现并发,即只用一个主线程(此时可利用的CPU只有一个)情况下实现并发: 并发的本质:切换+保存状态 CPU正在运行一个任务,会在两种情况下切走去执行其他任务(切换有操作系统强制控制 ...
随机推荐
- Django4全栈进阶之路9 STATIC静态文件路径设置、MEDIA媒体文件路径设置
在 Django 4 中,可以在 settings.py 文件中设置 STATICFILES_DIRS 来指定应用程序静态文件所在的文件夹路径,设置 STATIC_ROOT 来指定收集所有应用程序静态 ...
- 前端开发如何更好的避免样式冲突?级联层(CSS@layer)
作者:vivo 互联网前端团队 - Zhang Jiqi 本文主要讲述了CSS中的级联层(CSS@layer),讨论了级联以及级联层的创建.嵌套.排序和浏览器支持情况.级联层可以用于避免样式冲突,提高 ...
- 在SQL中将特定的数据始终排在第一行
将特定的数据始终排在第一行 第一种方式: select * from ( select Id,1 num from InquiryPurchaseProduct where Id = 50 union ...
- 尤雨溪创立 Vue.js 的心路历程纪录片
Show More 本文分享自微信公众号 - 生信科技爱好者(bioitee).如有侵权,请联系 support@oschina.cn 删除.本文参与"OSC源创计划",欢迎正在阅 ...
- hosts文件妙用,提升网站访问速度!
一.背景 在讲解hosts文件之前,我们先了解下IP地址与域名的关系. 1.IP地址与域名的关系 IP(Internet Protocol)是一种规定互联网中数据传输的协议,每台连接到互联网中的计算机 ...
- k8s实战案例之基于StatefulSet控制器运行MySQL一主多从
1.前言 Pod调度运⾏时,如果应⽤不需要任何稳定的标示.有序的部署.删除和扩展,则应该使⽤⼀组⽆状态副本的控制器来部署应⽤,例如 Deployment 或 ReplicaSet更适合⽆状态服务需求, ...
- SQL专家云快速解决阻塞
背景 当数据库突然产生严重阻塞时,运维人员要快速找到阻塞的源头并处理,让业务快速恢复.但是大多数运维人员只掌握了sp_who2.sp_lock等简单的语句,存在以下不足: 找不到真正的源头,过程中会误 ...
- 洛谷 P8179 Tyres
滴叉题/se/se 题意 直接复制了 有 \(n\) 套轮胎,滴叉需要用这些轮胎跑 \(m\) 圈.使用第 \(i\) 套轮胎跑的第 \(j\) 圈(对每套轮胎单独计数)需要 \(a_i+b_i(j- ...
- maven从远程仓库下载依赖包失败(因权限问题导致)
背景 在学习rocketMq时,编译官方提供的可视化项目:rocketmq-dashboard,频频失败,报以下错误 Could not transfer artifact org.apache.ro ...
- .Net Core 如何数据导出 Excel?(EPPlus->OfficeOpenXml 实现固定列和动态列导出)
〇.前言 对于将数据以 Excel 表格文件输出,还是比较常用的,也存在诸多情况,比如列固定或不固定.数据类型为 List<T>或 Json 对象等. 本文通过包 OfficeOpenXm ...