弄懂goroutine调度原理
goroutine简介
golang语言作者Rob Pike说,“Goroutine是一个与其他goroutines 并发运行在同一地址空间的Go函数或方法。一个运行的程序由一个或更多个goroutine组成。它与线程、协程、进程等不同。它是一个goroutine“。
- goroutine通过通道来通信,而协程通过让出和恢复操作来通信;
- goroutine 通过Golang 的调度器进行调度,而协程通过程序本身调度;
简单的说就是Golang自己实现了协程并叫做goruntine(本文称Go协程),且比协程更强大。
goroutine调度原理
上面说到Go协程是通过Golang的调度器进行调度的,其中调度器的线程模型为两级线程模型。
有关两级线程模型的介绍,可以看这篇文章
我们来看下Golang实现的两级线程模型是怎样的。首先要知道这三个字母代表的含义
- M:代表内核级的线程
- P:全程Processor,代表运行Go协程所需要的资源(上下文环境)
- G:代表Go协程

我们先看下为实现调度Golang定义了这些数据结构存M,P,G
| 名称 | 作用范围 | 描述 |
|---|---|---|
| 全局M列表 | Go的运行时 | 存放所有M的单向链表 |
| 全局P列表 | Go的运行时 | 存放所有P的数组 |
| 全局G列表 | Go的运行时 | 存放所有G的切片 |
| 调度器的空闲M列表 | 调度器 | 存放空闲M的单向链表 |
| 调度器的空闲P列表 | 调度器 | 存放空闲P的单向链表 |
| 调度器的自由G列表 | 调度器 | 存放自由G的单向链表(有两个) |
| 调度器的可运行G队列 | 调度器 | 存放可运行G的队列 |
| P的自由G列表 | 本地P | 存放当前P中自由G的单向链表 |
| P的可运行G队列 | 本地P | 存放当前P中可运行G的队列 |
然后从上往下解析Go的两级线程模型图
(1)M和内核线程之间是一对一的关系,一个M在其生命周期中,只会和一个内核线程关联,所以不会出现对内核线程的频繁切换;
Golang的运行时执行系统监控和垃圾回收等任务时候会导致创建M,M空闲时不会被销毁,而是放到一个
调度器的空闲M列表中,等待与P关联,M默认数量为10000
(2)P和M之间是多对多的关系,P和G之间是一对多的关系,他们的关联是易变的,由Golang的调度器完成调度;
Golang的运行时按规则调度,让P和不同的M建立或断开关联,使得P中的G能够及时获得运行时机
(3)P的数量默认为CPU总核心数,最大为256,当P没有可运行的G时候(P的可运行G队列为空),P会被放到调度器的空闲P列表中,等待M与它关联;
P有可能会被销毁,如运行时用runtime.GOMAXPROCS把P的数量从32降到16时,剩余16个会被销毁,它们原来的G会先转到调度器
可运行的G队列和自由G列表
(4)每个P中有可运行的G队列(如图中最下面的那行G)和自由G列表(图中未画出来),当G的代码执行完后,该G不会被销毁,而是被放到P的自由G列表或调度器的自由G列表。如果程序新建了Go协程,调度器会在自由G列表中取一个G,然后把Go协程的函数赋值到G中(如果自由G列表为空,就创建一个G);
可见Golang调度器在调度时很大程度复用了M,P,G
(5)在Go程序初始化后,调度器首先进行一轮调度,此时用M去搜索可运行的G。其中我们的main函数也是一个G,找到可运行的G后就执行它;
至于怎么找可运行的G呢?答案是到处找,想尽办法找(这里只列出一部分地方)。
- 从
本地P的可运行的G队列找- 从
调度器的可运行的G队列找- 从
其他P的可运行的G队列找
(6)P的可运行G队列最大只能存放长度为256的G,当队列满后,调度器会把一半的G转到调度器的可运行G队列。
系统监控
上面大概描述了关于goroutine调度的流程。现在还存在一个问题,那就是当Go协程很多(并发量大)时候,显然G是不能一直执行下去的,因为也需要把执行机会留给其他的G。此时Golang运行时的系统监控就起作用了。
一般情况,当G运行时间超过10ms后,该G就会被系统告知需要停止了,让其他G运行。(这里情况比较复杂,并不能确保每个G都能被公平执行)
以下特殊情况该G不需要停止
- P的可运行G队列为空(没有其他G可运行)
- 有空闲的M在寻找可运行的G(没有其他G可运行)
- 空闲的P(还有P闲着)
总结
Golang以两级线程实现模型,自己实现goruntine和调度器,优势在于并行和非常低的资源使用。
主要体现:
- 内存消耗方面(每个Go协程占的内存远小于线程占的内存)
- 切换(调度)开销方面
- 线程切换涉及模式切换(从用户态切换到内核态)
此外,Go协程执行任务完成的顺序并不都是按我们预期的那样(程序不加以控制的情况下),特别在一些耗时较长的任务中。且每个Go协程执行的时间也不是绝对公平的。
如有错误地方,还请狂喷!
弄懂goroutine调度原理的更多相关文章
- go语言之行--golang核武器goroutine调度原理、channel详解
一.goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式非常的简单,只需使用go关键字 ...
- [GO语言的并发之道] Goroutine调度原理&Channel详解
并发(并行),一直以来都是一个编程语言里的核心主题之一,也是被开发者关注最多的话题:Go语言作为一个出道以来就自带 『高并发』光环的富二代编程语言,它的并发(并行)编程肯定是值得开发者去探究的,而Go ...
- 一篇文章彻底弄懂Base64编码原理
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...
- 一篇文章彻底弄懂Base64编码原理(转载)
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...
- 知识扩展——(转)一篇文章彻底弄懂Base64编码原理
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成 ...
- 彻底弄懂jQuery事件原理二
上一篇说到,我们在最外层API的on,off,tiggler,triggerHandler调用的是event方法的add,remove和tirgger方法,本篇就来介绍event辅助类 \ 先放个图, ...
- 彻底弄懂jQuery事件原理一
jQuery为我们提供了一个非常丰富好用的事件API,相对于浏览器自身的事件接口,jQuery有以下特点: 1. 对浏览器进行了兼容性处理,用户使用不需要考虑浏览器兼容性问题 2. 事件数据是保持在内 ...
- 图解Go协程调度原理,小白都能理解
阅读本文仅需五分钟,golang协程调度原理,小白也能看懂,超实用. 什么是协程 对于进程.线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度.协程,又称微线程,纤程.英文名Corouti ...
- Golang/Go goroutine调度器原理/实现【原】
Go语言在2016年再次拿下TIBOE年度编程语言称号,这充分证明了Go语言这几年在全世界范围内的受欢迎程度.如果要对世界范围内的gopher发起一次“你究竟喜欢Go的哪一点”的调查,我相信很多Gop ...
随机推荐
- Element UI系列:Upload图片自定义上传
HTML部分代码 Javascript部分代码 CSS代码 样式部分可以自由调整 主要实现的原理是利用 http-request 的属性对上传进行自定义
- Asp.Net Core WebAPI+PostgreSQL部署在Docker中
PostgreSQL是一个功能强大的开源数据库系统.它支持了大多数的SQL:2008标准的数据类型,包括整型.数值值.布尔型.字节型.字符型.日期型.时间间隔型和时间型,它也支持存储二进制的大对像, ...
- 前端项目优化 -Web 开发常用优化方案、Vue & React 项目优化
github github-myBlob 从输入URL到页面加载完成的整个过程 首先做 DNS 查询,如果这一步做了智能 DNS 解析的话,会提供访问速度最快的 IP 地址回来 接下来是 TCP 握手 ...
- ansible模块介绍之ios_command
一.模块简介 ios_command此模块将任意命令发送到ios节点并返回设备读取的结果 此模块不支持在配置模式下使用,即只支持在用户模式>和特权模式#下使用 官方文档地址:https://do ...
- .NET Core下操作Git,自动提交代码到 GitHub
.NET Core 3.0 预览版发布已经好些时日了,博客园也已将其用于生产环境中,可见 .NET Core 日趋成熟 回归正题,你想盖大楼吗?想 GitHub 首页一片绿吗?今天拿她玩玩自动化提交代 ...
- python 之os模块用法大全
Python的标准库中的os模块包含普遍的操作系统功能.这个模块的作用主要是提供与平台无关的功能.也就是说os模块能够处理平台间的差异问题,使得编写好的程序无需做任何改动就能在另外的平台上运行 这边给 ...
- idea(java)实用开发插件
Idea常用的插件: mybatisX, ---------------- (Alt + enter) codeGlace, Lombok, sonarlint, translation, ...
- Spring学习之旅(八)--SpringMVC请求参数
现在我们已经完成了一个无参的接口了,但是应用中有很多需要携带参数的场景,我们来看看 ** SpringMVC** 对它的支持. 参数绑定 SpringMVC 提供了一种绑定机制,通过这个机制可以从请求 ...
- 支持向量机 (一): 线性可分类 svm
支持向量机(support vector machine, 以下简称 svm)是机器学习里的重要方法,特别适用于中小型样本.非线性.高维的分类和回归问题.本系列力图展现 svm 的核心思想和完整推导过 ...
- 【CocosBuilder】学习笔记目录
从2019年8月底开始学习CocosBuilder. CocosBuilder 学习笔记(1) CCBReader 解析.ccbi文件流程 CocosBuilder 学习笔记(2) .ccbi 文 ...