深入理解 Golang: Goroutine 协程

news2024/10/6 12:34:49

进程用来分配内存空间,是操作系统分配资源的最小单位;线程用来分配 CPU 时间,多个线程共享内存空间,是操作系统或 CPU 调度的最小单位;协程用来精细利用线程。协程就是将一段程序的运行状态打包,可以在线程之间调度。或者说将一段生产流程打包,使流程不固定在生产线上。协程不是被操作系统内核所管理,而完全是由程序所控制。

Go 中协程的本质

协程在 Go 内部的表示如下:

type g struct {
 stack        stack   // 协程栈
 sched        gobuf   // 目前程序运行现场
 atomicstatus atomic.Uint32 // 协程状态
 goid         uint64 // 协程 id
 
 // 。。。省略一些其他属性
 
}

type stack struct {
 lo uintptr
 hi uintptr
}

type gobuf struct {
 sp   uintptr // 栈指针,指向当前协程运行到哪个地方
 pc   uintptr // 程序计数器,记录运行到了哪行代码
 g    guintptr
 ctxt unsafe.Pointer
 ret  uintptr
 lr   uintptr
 bp   uintptr // for framepointer-enabled architectures
}

在这里插入图片描述

而线程的描述为一个 m 结构体:

type m struct {
 g0      *g     // goroutine with scheduling stack
 curg    *g     // current running goroutine

 mOS
  // 。。。省略一些其他属性
}
  • runtime 中将操作系统线程抽象为 m 结构体
  • g0 协程,操作调度器
  • curg 记录当前运行的协程
  • mOS 记录操作系统线程信息

单线程循环 Go 0.x
首先进入 g0 stack,执行 schedule() 方法,在 schedule 方法中调用 execute() 方法,execute 中再调用 gogo() 方法,gogo 方法为汇编语言编写,针对不同平台提供不同处理方法,接着通过 gogo 方法,从全局队列 runnable queue 中获取任务协程 g,进入到用户自定义的业务方法中。此时使用业务协程 g 自己的栈记录调用、跳转关系、本地变量等信息。

业务逻辑方法执行完成后会回退到 goexit() 的栈帧,goexit 会进行栈的切换,切换到 g0 stack,继续执行 schedule 方法链,不停地将 runnable queue 中的业务方法协程 g 取出执行。

多线程循环 Go 1.0
在单线程的基础上,多个线程的标准调度循环同时从 runnable queue 中取出协程 g 执行。注意,为保证协程安全,runnable queue 需要加锁。

存在的问题

  1. 协程串行执行,无法并发,会有阻塞现象。
  2. 多线程并发时,会抢夺全局队列的全局锁。

G-M-P 调度模型

前面说到,多线程从全局队列中取协程出来执行时,需要对这个队列加锁,如果每个线程 m 每次获取锁后,只从中取一个 g,则会造成很大开销,存在极大的性能问题。一个朴素的思想就是,每次取多个,将这些协程维护在一个自己的本地队列 p 中,本地队列中的全部 g 执行完后,再去全局队列抓取一堆。
在这里插入图片描述
这个本地队列 p 在 Go 中的表示如下:

type p struct {
 // 指向服务的线程
 m           muintptr   // back-link to associated m (nil if idle)
 // Queue of runnable goroutines. Accessed without lock.
 runqhead uint32
 runqtail uint32
 // 队列,存放 g
 runq     [256]guintptr
 // 下一个可用协程指针
 runnext guintptr
 // 。。。省略其他属性
}
  • P 作为 M 与 G 的中介,承担’送料’的作用。
  • P 持有一些 G,使得每次获取 G 不用去全局找。
  • P 大大减少了并发冲突状况。

新建协程
创建一个新协程时,会随机查找一个 P,将该协程放到 P 的 runnext(优先执行),如果本地队列已经满了,则将新协程放到全局队列。

协程阻塞-触发切换
P 中本地队列里存在长耗时任务时,会阻塞后续协程,造成饥饿现象,一种做法是内部调用 runtime.gopark() 方法,让当前大任务进入等待状态,回到 execute() 继续执行后续操作。另外也可以完成系统调时后挂起或主动挂起,这里的主动的含义是用户自己的方法去触发 runtime.gopark()。
在这里插入图片描述
在极端情况下,本地队列中的协程全部为耗时任务,则会造成全局队列 runnable queue 饥饿问题,那么此时需要同时调度本地队列和全局队列:
在这里插入图片描述
runtime 中的具体做法是:

func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
 // 。。。
 
 // Check the global runnable queue once in a while to ensure fairness.
 // Otherwise two goroutines can completely occupy the local runqueue
 // by constantly respawning each other.
 if pp.schedtick%61 == 0 && sched.runqsize > 0 {
  lock(&sched.lock)
  gp := globrunqget(pp, 1)
  unlock(&sched.lock)
  if gp != nil {
   return gp, false, false
  }
 }
 
 // 。。。
}

执行 61 次线程循环后,gp := globrunqget(pp, 1) 去全局协程队列里拿 1 个协程进本地队列。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/704546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea连接MYSQL报错汇总

idea连接MYSQL报错汇总 【The last packet sent successfully to the server was 0 milliseconds ago.The driver has not received any packets from the server.】 原因:mysql57 运行在非默认端口号 解决: 1.测试本地连接mysqsl57 打开C:\ProgramDa…

管理类联考——数学——技巧篇——公式——函数、方程与不等式

集合 A 有 n 个元素,则集合 A 的子集个数为 2 n 2^n 2n个,真子集个数为 2 n − 1 2^n-1 2n−1个,非空子集个数为 2 n − 1 2^n-1 2n−1个,非空真子集个数为 2 n − 2 2^n-2 2n−2个。抛物线 y a x 2 b x c yax^2bxc yax2bxc与轴…

ST STM32H723ZGTx - NUCLEO-H723ZG DMAMUX_RequestGen例程重现/消化/改进

文章目录 ST STM32H723ZGTx - NUCLEO-H723ZG DMAMUX_RequestGen例程重现/消化/改进概述笔记问题的难点为了确定程序流程, 加入ITM将CubeMX升级到最新将CubeMX说明书翻一下CubeMX配置芯片功能实验工程主要代码.sct文件main.c中断实现文件 stm32h7xx_it.c.ioc补充END ST STM32H72…

探索网络通信核心技术,手写TCPIP用户态协议栈,让性能飙升起来!

一、DPDK简介 DPDK(Data Plane Development Kit)是一个开源的数据平面开发工具包,它提供了一组C语言库和驱动程序,用于快速开发高性能的数据平面应用程序。DPDK使用用户空间的方式来实现网络数据包处理,从而避免了传统…

海气相互作用 - 全球水循环过程及其量级

全球水循环过程及其量级 单位:Sv106m3/s,大气/陆地/海洋(103 km3)径流1.3 Sv≈台湾暖流1.1 Sv≈白令海峡0.9-1.1 Sv 从涡度平衡的角度说明为什么大洋强化发生在西边界而非东边界 有且只有在大洋西边界强化,才可以使得摩擦力产生一个正的涡…

【线程池】Java线程池的内部类Worker详解

目录 一、简介 二、Worker类对象的类图 三、Worker类对象的解释 4.2 Worker继承自AQS有何意义? 四、Worker的主要代码 4.1 运行worker 4.2 worker和ThreadPool的关系 五、Worker源码分析 5.1 Worker实现接口Runnable,执行run方法 5.2 核心方法…

Mysql主从原理

1.为什么要主从? 什么东西都有丢失或损坏的可能,所以备份是一个重要的手段。 2.备份机制? 对于主从库的分工: "主写从读" 中继日志文件我的理解是一个大的临时区,主库推送过来的数据不用同步进行到从库,这…

零基础学会Python编程——数据也分类:常见数据类型

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录 ​编辑 前言 学习目标 一.常见数据类型 1.数字类型 (1&#xff09…

Can’t connect to MySQL server on ‘localhost’ (10061)

标题:Can’t connect to MySQL server on ‘localhost’ (10061) 一种情况是,mysql服务没有开启, 解决方式:以管理员身份进入cmd,然后输入net start mysql,注意这里的mysql,指的是你的mysql服务的名称&am…

万字长文,SpringSecurity实现权限系统设计

RBAC权限分析 RBAC 全称为基于角色的权限控制,本段将会从什么是RBAC,模型分类,什么是权限,用户组的使用,实例分析等几个方面阐述RBAC 思维导图 绘制思维导图如下 什么是RBAC RBAC 全称为用户角色权限控制&#xff…

MySQL笔记之一致性视图与MVCC实现

一致性读视图是InnoDB在实现MVCC用到的虚拟结构,用于读提交(RC)和可重复度(RR)隔离级别的实现。 一致性视图没有物理结构,主要是在事务执行期间用来定义该事物可以看到什么数据。 一、Read View 事务在正…

怎样高标准地个性化您的电商业务?教你一招搞定

怎样高标准地个性化您的电商业务?教你一招搞定! 想要增加销售额和打造一个客户喜爱的品牌,电子商务个性化是您成功的关键。但是,个性化您的在线购物体验的各个方面这个过程简直令人生畏。但是别怕,我将在这里告诉大家利…

计算机 - - - windows电脑搭建FTP局域网文件传输服务器

控制面板,打开程序和功能,启用或关闭Windows功能。 勾选这几个 然后点确定,应用更改成功后重启电脑生效。 打开IIS管理器。 添加ftp站点。 输入ftp站点名称,选择文件存放的物理路径。 ip地址设置为当前电脑的ip地址&#x…

重识Flutter — 探索Slivers的奇妙世界(综合实例)

前言 在前三篇文章中,从为什么要使用Sliver,再根据使用频率逐个解析Slivers系列的组件。相信您已经入门了Sliver的世界。为了更好的将Slivers相关的组件结合起来使用,本文将通过一个综合的案例来帮助你理解。 源代码:https://ww…

【动态规划】LeetCode 583. 两个字符串的删除操作 Java

583. 两个字符串的删除操作 我的代码,错误代码,只考虑到了字母出现的次数,没有考虑到两个字符串中字母出现的顺序 class Solution {public int minDistance(String word1, String word2) {int[] arr1 new int[26];int[] arr2 new int[26];…

Fiddler抓包工具笔记

一、简介 Fiddler代理相当于中介的角色 快捷键ShiftF5去缓存刷新 二、抓包 1. 设置过滤器 没有设置过滤器的话,会抓所有的包,非常乱会混淆 隐藏包含这些内容的URL 2. 快速定位到需要的包 点击:Webforms菜单 界面分析: …

SQlite3 编译

参考博客:https://blog.csdn.net/flowerspring/article/details/121268403 1.下载C源码以及def文件https://www.sqlite.org/download.html 2. 下载完成之后解压 sqlite-amalgamation获取C源码,解压sqlite-dll-win32-xx获取里面的def文件。 3.新建sqlite…

前端vue入门(纯代码)18

不管何时何地,永远保持热爱,永远积极向上!!! 【20.尚硅谷GitHub搜索案例_vue-resource实现】 1.vue-resource vue-resource 是 vue 中一个用于发送请求的插件。 vue 发送请求推荐使用 axios ,vue-resourc…

2023上半年软考系统分析师科目一整理-23

2023上半年软考系统分析师科目一整理-23 对于如下所示的序列图所描述的场景,最适合于采用的设计模式是(30);该模式适用的场合是(31)。 A.Visitor B.Strategy C.Observe…

TI AM64x工业核心板规格书(双核ARM Cortex-A53 + 单/四核Cortex-R5F + 单核Cortex-M4F,主频1GHz)

1 核心板简介 创龙科技SOM-TL64x是一款基于TI Sitara系列AM64x双核ARM Cortex-A53 单/四核Cortex-R5F 单核Cortex-M4F设计的多核工业级核心板,通过工业级B2B连接器引出5x TSN Ethernet、9x UART、2x CAN-FD、GPMC、PCIe/USB 3.1等接口。核心板经过专业的PCB Layo…