Go并发机制

​ 协程:一个线程可以对应多个协程,协程串行运行在用户空间。协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。

​ Go摒弃线程、进程、协程,提出goroutine:

  • M(machine):一个M代表一个内核线程(与KSE一一对应)
  • P(processor):一个P代表执行一个Go代码片段所必须的资源(上下文环境)
  • G(goroutine):一个G代表一个Go代码片段。

​ 一个M与一个P关联之后,就形成一个G的运行环境(内核线程+上下文环境)。M与KSE一对一,M与P总是一对一(当一个M因系统调用阻塞(运行的G进入系统调用),此时P会与M分离开来,并与新建/空闲的M关联),P与G一对多

操作 作用
runtime/debug.SetMaxThreads 设置M的最大数量
runtime.GOMAXPORCS 设置P的最大数量(可运行G队列的数量)

一、M、P、G

——M(Machine)

​ 系统维护一个全局M列表(runtime.allm) 调度器维护一个空闲M列表(runtime.sched.midle)

——P(Processor)

​ 系统维护一个全局P列表(runtime.allp) 调度器维护一个空闲P列表(runtime.sched.pidle)

​ 每个P维护一个可运行G队列(runtime.p.runq)(队列满则会分出一半给调度器可运行G队列)与一个自由G列表(runtime.p.gfree)(已经运行完成的G,在欲启用一个go语句时,会复用该列表中的G。当P中的自由G列表元素过多或过少时,调度器的自由G列表会与其进行转移)

状态 说明
Pidle 当前P未与任何M存在关联
Prunning 当前P正在与某个M关联
Psyscall 当前P中运行的那个G正在进行系统调用
Pgcstop 运行时系统需要停止调度(系统开始垃圾回收的一些步骤,会将全部P设为此状态)
Pdead 当前P已经不会再被使用(调整最大P数量后,多余的P会被设为此状态)


​ 非dead状态的P在系统欲停止调度时都会被置于Pgcstop状态。等到需要重启调度时,会被统一置于Pidle状态,即公平的接受再次调度。同时进入dead状态的P,其可运行的G与自由的G都会被转移到调度器的可运行G列表与自由G列表中。

——G(goroutine例程)

​ 系统维护一个全局G列表(runtime.allgs)

​ 调度器维护一个可运行G列表(runtime.sched.runqhead runtime.sched.runqtail) 一个自由G列表(runtime.sched.gfreeStack runtime.sched.gfreeNoStack)

状态 说明
Gidle 刚被新分配,还未初始化
Grunnable 正在可运行队列中等待运行
Grunning 当前G正在运行
Gsyscall 当前G正在执行系统调用
Gwaiting 当前G正在阻塞
Gdead 当前G正在闲置
Gcopystack 当前G的栈正在被移动


​ Gdead不同于Pdead,前者可以加入自由列表等待再次复用,后者只会被销毁。

二、调度器

​ Go调度器不是运行在某个专用内核线程中的程序,调度器会运行在几乎所有已存在的M(内核线程)中。

​ 调度器基本数据结构:空闲M列表,空闲P列表,可运行G队列,自由G列表,此外还有几个重要字段与需要停止调度的任务(Stop the world,STW)有关。

字段名 数据类型 作用
gcwaiting uint32 是否需要因一些任务而停止调度(比如垃圾回收)
stopwait int32 需要停止但仍未停止的P数量
stopnote note 实现与stopwait相关的事件通知机制

​ 在停止调度前,gcwaiting被置为1,调度任务在发现这一状态后,将当前P状态置为Pgcstop并将stopwait字段减一。当stopwait减为0时,说明所有P已置为Pgcstop,此时利用stopnote唤醒待执行的任务(比如垃圾回收),之后恢复gcwaiting为0。

字段名 数据类型 作用
sysmonwait unit32 在停止调度期间系统监控任务是否在等待
sysmonnote note 实现与sysmonwait相关的事件通知机制

​ 这两个字段针对系统监测任务,即在执行需要停止调度的任务之前,也需要停止系统监测任务。系统监测程序发现所有P都已经闲置或gcwaiting不为0,会将sysmonwait置为1,并使用sysmonnote暂停自身,结束后,再恢复这两个状态。

一轮调度

​ 在启动Go程序并完成初始化后,会启动一轮调度使得封装了main函数的G可以被调度运行,在G运行阻塞、结束、退出系统调用后都会引发一轮调度。

  • M与G的成对锁定,是为了CGO准备。即C的函数库会将数据存储于内核线程,所以为了放止数据丢失,只能在一段时期内将G与特定M进行关联。

  • 当调度器为某个M1寻找到可执行G,但是检查到某个M2已经与该G锁定,则会立刻停止调度并停止M2,并将G交由M2运行(实际上是把M1的P交由M2),M1则寻找其他可执行G。——这意味着M2在运行锁定的G前,不会做其他事,即资源被浪费。

  • 全力查找可运行的G:若还未找到G,则调度器会停止该M,并在以后特定时刻唤醒重新查找。还未找到可运行G的M称为自旋状态

  • 启用或停止M

    (1)调度器调度一个M会预先检查其是否与某个G锁定;如果有,则会调用stoplockedm解除当前M与P的关联并将P转手,并暂停当前M的执行。

    (2)调度器为M发现一个可运行的G,但是该G已经被其他M锁定。会调用startlockedm将本地M的P转手给锁定的M,并暂停本地M的运行。放入空闲M列表。

    (3)调度器发现有STW任务时,会调用gcstopm停止当前M。即释放本地P(置为Pgcstop)并调用stopm。

    (4)只有当有新工作,并且由空闲P时,调用startm才可以执行一个M。

    操作 作用
    stopm() 停止当前M的执行
    gcstopm() 为STW任务让路,停止当前M,执行完毕后会被唤醒
    stoplockedm() 停止已与某个G锁定的M的执行,直到这个G可运行
    startlockedm(gp *g) 唤醒与gp锁定的那个M
    startm(p *p, spining bool) 唤醒或创建一个M去关联P并开始执行
三、其他几个要点

——g0、m0

​ g0:系统中每个M拥有的特殊G。g0所拥有的内存称为M的调度栈,对应于内核中线程的栈,即OS线程栈。用于执行调度、垃圾回收、栈管理等。

​ gsignal:系统中每个M拥有的特殊G,用来处理信号。即信号栈

​ m0:Go程序的第一个内核线程runtime.g0,用于执行引导程序。

——调度器锁和原子操作

​ 每个M都有可能执行调度任务,而这些任务可能会并发进行,所以需要在读写一些全局变量时进行调度器锁保护。比如sched.stopwait(STW任务时记录需要停止的M)、sched.nmidle(对空闲M计数)、对核心元素容器进行存取(runtime.allp, runtime.sched.runqhead)。

​ 同时也采用原子操作保护一些变量。比如sched.spining(对自旋M计数)、sched.ngsys(对系统G计数)、切换G状态。

——调整GC

​ Go的GC基于CMS(Concurrent Mark-Sweep)算法。调度器会适时调度GC相关任务执行,系统监测任务也会在必要时强制执行。有三种执行模式:

  • gcBackgroundMode:并发执行垃圾收集和清扫。
  • gcForceMode:串行的执行垃圾收集(即执行时停止调度),并发的执行垃圾清扫。
  • gcForceBlockMode:串行的执行垃圾收集和清扫。

Go学习笔记—并发高级相关推荐

  1. matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色

    一起来学matlab-matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色 觉得有用的话,欢迎一起讨论相互学习~ 参考书籍 <matlab 程 ...

  2. RabbitMQ学习笔记(高级篇)

    RabbitMQ学习笔记(高级篇) 文章目录 RabbitMQ学习笔记(高级篇) RabbitMQ的高级特性 消息的可靠投递 生产者确认 -- confirm确认模式 生产者确认 -- return确 ...

  3. 编译原理学习笔记2——高级程序设计语言概述

    编译原理学习笔记2--高级程序设计语言概述 2.1常用的高级程序设计语言 2.2程序设计语言的定义 2.2.1语法 2.2.1语法 2.2.3程序语言的基本功能和层次机构 2.2.4程序语言成分的逻辑 ...

  4. Learn Git Branching 学习笔记(高级话题篇)

    目录 一.高级话题篇 1.多分支rebase 2.选择父提交记录 3.纠缠不清的分支 Git的一些技术.技巧与贴士集合在上一篇文章中 Learn Git Branching 学习笔记(Git 技术.技 ...

  5. Bash Cookbook 学习笔记 【高级】

    Read Me 本文是以英文版<bash cookbook> 为基础整理的笔记,力求脱水 [高级]部分,涉及脚本安全.bash定制.参数设定等高阶内容 本系列其他两篇,与之互为参考 [基础 ...

  6. MySQL学习笔记_9_MySQL高级操作(上)

    MySQL高级操作(上) 一.MySQL表复制 create table t2 like t1;               #复制表结构,t2可以学习到t1所有的表结构 insert into t2 ...

  7. Python学习笔记:高级特性

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  8. java task和thread_【Java学习笔记-并发编程】线程与任务

    前言 最近在看一些Java15的并发.线程调度以及一些实现方案的东西,虽然很多东西还是 1.5 的,但还是很有收获. 一.线程与任务 Java中,要用线程来执行任务,线程可以说是任务的容器.没有线程的 ...

  9. MySQL学习笔记_10_MySQL高级操作(下)

    MySQL高级操作(下) 五.MySQL预处理语句 1.设置预处理stmt,传递一个数据作为where的判断条件 prepare stmt from "select * from table ...

最新文章

  1. 使用容器和数据库克隆进行数据库迁移
  2. python做好的程序如何变成小程序-使用python编写简单的小程序编译成exe跑在win10上...
  3. Python 位运算符号
  4. 以操作系统的角度述说线程与进程
  5. java 实现获取支付宝授权获取会员信息
  6. python hist函数_虎哥的python小技巧放送之绘制统计图(2)
  7. 比尔盖茨的30条经典语录
  8. linux网络编程之SCTP套接字常用接口
  9. jtable隐藏全部_全部隐藏!
  10. VS2010与QT的集成开发环境
  11. php curl模拟https请求
  12. 大工14秋《计算机应用基础》在线测试2,2017大工《计算机应用基础》在线测试2答案.doc...
  13. eve可以在linux运行吗,ubuntu下为eve游戏搭载 wine环境
  14. 随笔 - C/C++
  15. 高性能反向代理软件HAProxy(一)之基本概念
  16. python web 模版引擎集合
  17. 考研复试-计算机网络速记知识点
  18. 【冷门快捷键】设置VSCode终端大小最小化快捷键Alt+PageUp/PageDown、编辑代码窗口切换大小快捷键Alt+数字键盘“+”、Alt+数字键盘“-”、Alt+数字键盘“0”
  19. MAX96706开发板POC电路分析
  20. 排队服务系统仿真研究计算机模拟,基于蒙特卡洛方法排队系统性能的仿真优化研究...

热门文章

  1. ES6-7 - 箭头函数的实质、箭头函数的使用场景
  2. 61-1 认识webpack
  3. JVM(4)之 使用MAT排查堆溢出
  4. 卓同学的 Swift 面试题
  5. Java使用线程并发库模拟弹夹装弹以及发射子弹的过程
  6. POJ-1556 The Doors 线段相交+最短路
  7. [XMOVE自主设计的体感方案] XMove Studio管理系统(二)应用开发API简要介绍
  8. sqlite的数据导入 导出
  9. Python: pip升级报错了:You are using pip version 10.0.1, however version 20.3.3 is available.
  10. DVI和HDMI中的TMDS接口协议