Go学习笔记—并发高级
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学习笔记—并发高级相关推荐
- matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色
一起来学matlab-matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色 觉得有用的话,欢迎一起讨论相互学习~ 参考书籍 <matlab 程 ...
- RabbitMQ学习笔记(高级篇)
RabbitMQ学习笔记(高级篇) 文章目录 RabbitMQ学习笔记(高级篇) RabbitMQ的高级特性 消息的可靠投递 生产者确认 -- confirm确认模式 生产者确认 -- return确 ...
- 编译原理学习笔记2——高级程序设计语言概述
编译原理学习笔记2--高级程序设计语言概述 2.1常用的高级程序设计语言 2.2程序设计语言的定义 2.2.1语法 2.2.1语法 2.2.3程序语言的基本功能和层次机构 2.2.4程序语言成分的逻辑 ...
- Learn Git Branching 学习笔记(高级话题篇)
目录 一.高级话题篇 1.多分支rebase 2.选择父提交记录 3.纠缠不清的分支 Git的一些技术.技巧与贴士集合在上一篇文章中 Learn Git Branching 学习笔记(Git 技术.技 ...
- Bash Cookbook 学习笔记 【高级】
Read Me 本文是以英文版<bash cookbook> 为基础整理的笔记,力求脱水 [高级]部分,涉及脚本安全.bash定制.参数设定等高阶内容 本系列其他两篇,与之互为参考 [基础 ...
- MySQL学习笔记_9_MySQL高级操作(上)
MySQL高级操作(上) 一.MySQL表复制 create table t2 like t1; #复制表结构,t2可以学习到t1所有的表结构 insert into t2 ...
- Python学习笔记:高级特性
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- java task和thread_【Java学习笔记-并发编程】线程与任务
前言 最近在看一些Java15的并发.线程调度以及一些实现方案的东西,虽然很多东西还是 1.5 的,但还是很有收获. 一.线程与任务 Java中,要用线程来执行任务,线程可以说是任务的容器.没有线程的 ...
- MySQL学习笔记_10_MySQL高级操作(下)
MySQL高级操作(下) 五.MySQL预处理语句 1.设置预处理stmt,传递一个数据作为where的判断条件 prepare stmt from "select * from table ...
最新文章
- 使用容器和数据库克隆进行数据库迁移
- python做好的程序如何变成小程序-使用python编写简单的小程序编译成exe跑在win10上...
- Python 位运算符号
- 以操作系统的角度述说线程与进程
- java 实现获取支付宝授权获取会员信息
- python hist函数_虎哥的python小技巧放送之绘制统计图(2)
- 比尔盖茨的30条经典语录
- linux网络编程之SCTP套接字常用接口
- jtable隐藏全部_全部隐藏!
- VS2010与QT的集成开发环境
- php curl模拟https请求
- 大工14秋《计算机应用基础》在线测试2,2017大工《计算机应用基础》在线测试2答案.doc...
- eve可以在linux运行吗,ubuntu下为eve游戏搭载 wine环境
- 随笔 - C/C++
- 高性能反向代理软件HAProxy(一)之基本概念
- python web 模版引擎集合
- 考研复试-计算机网络速记知识点
- 【冷门快捷键】设置VSCode终端大小最小化快捷键Alt+PageUp/PageDown、编辑代码窗口切换大小快捷键Alt+数字键盘“+”、Alt+数字键盘“-”、Alt+数字键盘“0”
- MAX96706开发板POC电路分析
- 排队服务系统仿真研究计算机模拟,基于蒙特卡洛方法排队系统性能的仿真优化研究...
热门文章
- ES6-7 - 箭头函数的实质、箭头函数的使用场景
- 61-1 认识webpack
- JVM(4)之 使用MAT排查堆溢出
- 卓同学的 Swift 面试题
- Java使用线程并发库模拟弹夹装弹以及发射子弹的过程
- POJ-1556 The Doors 线段相交+最短路
- [XMOVE自主设计的体感方案] XMove Studio管理系统(二)应用开发API简要介绍
- sqlite的数据导入 导出
- Python: pip升级报错了:You are using pip version 10.0.1, however version 20.3.3 is available.
- DVI和HDMI中的TMDS接口协议