前言

  • 最近用到了一些 Golang 异步编程的地方,感觉 Golang 相对于其他语言(如 Java)对多线程编程的支持非常大,使用起来也非常方便。于是决定了解一下 Goroutine 的底层原理。
  • Goroutine 本质是协程,是实现并行计算的核心。只需要在对应的函数前加上 Go 关键词即可异步执行:
go func() {
}()

基本概念

  • 并发:一段时间内执行多个程序,即在一个 cpu 上切换着执行多项任务,宏观上是同时的,微观上是顺序执行
  • 并行:同时执行多个程序,即在多个 cpu 上同时运行不同任务,不需要上下文切换执行不同任务达到宏观同时进行的效果
  • 进程:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。是具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体
  • 线程:线程是进程的一个实体,是 cpu 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换较快,资源开销较少
  • 协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作执行者则是用户自身程序。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。Goroutine 就是一种协程

调度模型

  • Goroutine 并发调度通过 GPM 模型实现,包含四个结构:M、G、P、Sched:

    • M:代表内核级线程,一个 M 对应一个线程
    • G:代表一个 Goroutine,包含自己的栈、程序计数器等信息
    • P:指处理器,主要用途是用来执行 Goroutine,维护一个 Goroutine 队列,同时还有一个全局队列。每一个运行的 M 都必须绑定一个 P,就像线程必须在么一个 cpu 核上执行一样
    • Sched:代表调度器,维护 M 和 G 的队列和状态信息

  • 如上图,2 个 M,每个 M 拥有 1 个 P 和 1 个正在运行的 G。
  • P 的数量可以通过 GOMAXPROCS() 设置,代表有多少个 Goroutine 可以同时运行。

调用异步

  • 执行 go func() 时,会在队列尾部加入一个 Goroutine。
  • 如果此时还有空闲的 P,则创建一个 M。M 会启动一个底层线程,循环执行能找到的 G 任务。
  • G 任务的执行任务是:先从本地队列找,本地没有则从全局队列找。一次转移 num(G)/num(P) 个任务,再去其他 P 中获取队列一半的任务。

监视超时

  • 启动一个 G 时,会专门创建一个 Sysmon,用来监视和管理。记录所有 P 的 G 任务计数 schedtick(schedtick 会在每执行一个 G 任务后递增)。
  • 如果检查到 schedtick 一直没有递增,说明这个 P 一直在执行同一个 G 任务,如果超过一定时间(10ms),就在这个 G 任务的栈信息里加一个标记。
  • 内联函数执行时发现标记则中断自己,把自己加到队列末尾;非内联函数则会忽视标记,一直执行到结束。

中断恢复

  • 对于一个 G 任务中断后:中断时将寄存器信息保存在 G 对象里,再次执行时将栈信息复制到寄存器里,继续执行。

首次启动

  • 系统启动时主线程启动,第一个 M1 就是主线程,M1 会绑定一个 P。
  • main 函数作为第一个 Goroutine 执行。
  • main 里其他的 Goroutine 会绑定到当前 M1 的 P1 上。
  • 执行 main 里的 Goroutine 时,会创建新的 M2,新 M2 的初始 P2 本地任务队列时空的,会从 P1 取一些过来。
  • 然后依此类推直到 M 数量达到限制。

参考文献

Golang(三)Goroutine原理的更多相关文章

  1. 谈谈Golang中goroutine的调度问题

    goroutine的调度问题,同样也是我之前面试的问题,不过这个问题我当时并不是很清楚,回来以后立马查阅资料,现整理出来备忘. 有一些预备知识需要说明,就是操作系统中的线程.操作系统中的线程分为两种: ...

  2. [转]golang的goroutine调度机制

    golang的goroutine调度机制 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一直对goroutine的调度机制很好奇最近在看雨痕的golang源码分析基于go ...

  3. golang的goroutine与channel

    Golang的goroutine是非抢占式的, 令人相当蛋疼! 有痛不能呻吟...只能配合channel在各goroutine之间传递信号来实现抢占式, 而这形成了golang最灵活与最具性能的核心. ...

  4. 机器学习之决策树三-CART原理与代码实现

    决策树系列三—CART原理与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/9482885.html ID ...

  5. go语言之行--golang核武器goroutine调度原理、channel详解

    一.goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式非常的简单,只需使用go关键字 ...

  6. Golang/Go goroutine调度器原理/实现【原】

    Go语言在2016年再次拿下TIBOE年度编程语言称号,这充分证明了Go语言这几年在全世界范围内的受欢迎程度.如果要对世界范围内的gopher发起一次“你究竟喜欢Go的哪一点”的调查,我相信很多Gop ...

  7. Golang Context 的原理与实战

    本文让我们一起来学习 golang Context 的使用和标准库中的Context的实现. golang context 包 一开始只是 Google 内部使用的一个 Golang 包,在 Gola ...

  8. Golang控制goroutine的启动与关闭

    最近在用golang做项目的时候,使用到了goroutine.在golang中启动协程非常方便,只需要加一个go关键字: go myfunc(){ //do something }() 但是对于一些长 ...

  9. golang slice 切片原理

    golang 中的 slice 非常强大,让数组操作非常方便高效.在开发中不定长度表示的数组全部都是 slice .但是很多同学对 slice 的模糊认识,造成认为golang中的数组是引用类型,结果 ...

随机推荐

  1. 数据库IN查询参数化改造的方法

    // 批量查询的 2019-05-14 if (!string.IsNullOrWhiteSpace(Request["userCodes"])) { string userCod ...

  2. P2704 [NOI2001]炮兵阵地 (状压DP)

    题目: P2704 [NOI2001]炮兵阵地 解析: 和互不侵犯一样 就是多了一格 用\(f[i][j][k]\)表示第i行,上一行状态为\(j\),上上行状态为\(k\)的最多的可以放的炮兵 发现 ...

  3. CentOS 7.0 使用yum 安装 Mariadb

    第一步: 使用命令查看是否已经安装: mysql -u root -p 返回 Enter password:  时表示已经安装成功的,需要卸载安装. 第二步: 使用yum直接安装mariadb,注意带 ...

  4. element表格的滚动条在合计上边

    默认滚动条是在下边的,不好看,这里改一下 修改样式.完美解决: .el-table { overflow-x: auto; } .el-table__header-wrapper, .el-table ...

  5. JVM问题排查工具:Serviceability-Agent介绍

    本文首发于微信公众号:javaadu 简单介绍 构建高性能的Java应用过程中,必然会遇到各种各样的问题,像CPU飙高.内存泄漏.应用奔溃,以及其他疑难杂症,这时可以使用Serviceability ...

  6. Kustomize安装配置入门文档

    一,简介 kustomize是sig-cli的一个子项目,它的设计目的是给kubernetes的用户提供一种可以重复使用同一套配置的声明式应用管理,从而在配置工作中用户只需要管理和维护kubernet ...

  7. Cutting Bamboos(2019年牛客多校第九场H题+二分+主席树)

    题目链接 传送门 题意 有\(n\)棵竹子,然后有\(q\)次操作,每次操作给你\(l,r,x,y\),表示对\([l,r]\)区间的竹子砍\(y\)次,每次砍伐的长度和相等(自己定砍伐的高度\(le ...

  8. 网站调试时记得关闭火狐adblock插件

    由于特殊需要,xmyanke需要在网站右侧添加一个弹窗,第一个网站加上代码后可以正常显示,第二个网站却怎么也看不到图片,同样的安装方法为什么差别那么大呢?重新复制代码还是不行,再试一遍,依然如此,wi ...

  9. Linux 命令 mv

    mv 命令 --no-target-directory 参数确保对目录进行重命名而不是移动 https://www.gnu.org/software/coreutils/manual/html_nod ...

  10. C++stringstream使用

    链接 建议在使用是直接使用stringstream,不需要再去分输入用istringstream,输出用ostringstream.