前言

  • 最近用到了一些 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. 数据路由通信--ospf复习

    数据路由通信--ospf 复习 ospf 复习 今天来回顾一下ospf ospf全名叫做开放式最短路径优先协议,它是一种基于链路状态的内部网关路由协议.ospf支持区域的划分,内部的路由器使用spf最 ...

  2. JavaScriptCore在浏览器引擎中的位置

    因为随着JS这门语言的发展,JS的宿主越来越多,有各种各样的浏览器,甚至是常见于服务端的Node.js(基于V8运行). 2.       Webkit 源代码由三大模块组成: 1). WebCore ...

  3. kafka在zookeeper创建使用了哪些znode节点?

    我们都知道kafka利用zookeeper做分布式管理,具体创建使用了哪些znode节点呢? 答案均在源码的ZkData.scala文件中,具体路径如下: https://github.com/apa ...

  4. aspose.cells导出Demo

    /// <summary> /// 导出excel /// </summary> /// <param name="list"></par ...

  5. python 排序 桶排序

    算法思想: 桶排序将数组分到有限数量的桶里.然后每个桶里再分别排序(使用任何算法) 当要倍排序的数组内的数值时均匀分配的时候,桶排序使用线性时间O(n) 步骤: 根据最大值.最小值.桶内数据范围设定一 ...

  6. Math基础使用

    /* java.lang.Math类是数学相关的工具类,里面提供的大量静态方法,完成与数学运算的操作 public static double abs(double num):获取绝对值. publi ...

  7. ANT 的使用

    概述 ant 是一个将软件编译.测试.部署等步骤联系在一起加以自动化的一个工具,大多用于 Java 环境中的软件开发. 在与 Jmeter 生成的 jmx 文件配合使用中,ant 会完成jmx计划的执 ...

  8. Git恢复删除的分支

    1.使用 git reflog 命令查看显示整个本地仓储的commit,包括所有branch的commit,甚至包括已经撤销的commit. 2.找到我们想要恢复的分支 ,可以看到我们当时commit ...

  9. 【转载】Gradle学习 第九章:Groovy快速入门

    转载地址:http://ask.android-studio.org/?/article/17 To build a Groovy project, you use the Groovy plugin ...

  10. SpringMVC中控制器接收JSP页面表单的参数接收方式详解及细节注意(400错误)

    控制器方法中参数的接收 (1)以前的参数接收: String  param = req.getParameter(name): (2)SpringMVC简化这个操作,只需要给控制器方法添加参数即可 a ...