深入理解 Golang: Goroutine 协程

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

Go 中协程的本质

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

type g struct {stack        stack   // 协程栈sched        gobuf   // 目前程序运行现场atomicstatus atomic.Uint32 // 协程状态goid         uint64 // 协程 id// 。。。省略一些其他属性}type stack struct {lo uintptrhi uintptr
}type gobuf struct {sp   uintptr // 栈指针,指向当前协程运行到哪个地方pc   uintptr // 程序计数器,记录运行到了哪行代码g    guintptrctxt unsafe.Pointerret  uintptrlr   uintptrbp   uintptr // for framepointer-enabled architectures
}

在这里插入图片描述

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

type m struct {g0      *g     // goroutine with scheduling stackcurg    *g     // current running goroutinemOS// 。。。省略一些其他属性
}
  • 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 uint32runqtail uint32// 队列,存放 grunq     [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.hqwc.cn/news/1304.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java之路——带你了解Hibernate与基本过程

文章目录 前言一、Hibernate用来干嘛的二、Hibernate与mybatisHibernate的基本开发步骤 前言 Hibernate框架的发展可以追溯到2001年,它在过去的几年里获得了广泛的应用和持续的发展。 其中的发展演变: 初期版本(2001-2006年)&am…

css基础知识十:介绍一下CSS中的Grid网格布局?

一、是什么 Grid 布局即网格布局,是一个二维的布局方式,由纵横相交的两组网格线形成的框架性布局结构,能够同时处理行与列 擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系 这与之前讲到的flex一维…

【单片机】STM32F103C8T6 最小系统板原理图

STM32F103C8T6是一款基于ARM Cortex-M3内核的32位微控制器,由STMicroelectronics(ST)公司生产。它是STMicroelectronics的STM32系列微控制器中的一员,被广泛应用于嵌入式系统和电子设备中。 STM32F103C8T6单片机的主要特点和资源…

Windows11安装oneAPI和Visual Studio 2022配置Fortran并行环境

Windows11安装oneAPI和Visual Studio 2022配置Fortran并行环境 安装Visual Studio 2022 Community安装oneAPI建立Fortran工程项目测试建立单核运行的Fortran运行算例建立并行运行的Fortran运行算例 结语 安装Visual Studio 2022 Community 访问微软Visual Studio官网&#xff…

二叉树题目:单值二叉树

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:单值二叉树 出处:965. 单值二叉树 难度 3 级 题目描述 要求 如果二叉树每个结点都具有相同的值&am…

【Solr】中文分词配置

提示:在设置中文分词前需确保已经生成过core,未生成core的可以使用:solr create -c "自定义名称"进行定义。 未分词前的效果预览: 下载分词器: 下载地址: https://mvnrepository.com/artifact/com.github.m…

网络安全能力成熟度模型介绍

一、概述 经过多年网络安全工作,一直缺乏网络安全的整体视角,网络安全的全貌到底是什么,一直挺迷惑的。目前网络安全的分类和厂家非常多,而且每年还会冒出来不少新的产品。但这些产品感觉还是像盲人摸象,只看到网络安…

python spider 爬虫 之 解析 xpath 、jsonpath、BeautifulSoup (三)

BeautifulSoup 简称:bs4 BeautifulSoup跟lxml 一样,是一个html文档的解析器,主要功能也是解析和提取数据 优缺点 缺点:效率没有lxml的效率高 优点:接口接口人性化,使用方便 延用了css选择器 安装Beautifu…

项目——学生信息管理系统2

目录 用户类型,我们创建一个枚举类 在 org.xingyun.model 包下创建 UserType 枚举类 快速生成 get set 方法 修改代码,下拉框的内容,用我们的枚举类型 给登录按钮绑定事件 我们在 org.xingyun.utils 包下创建一个工具类 Eclipse 智能提…

四年之约不负等待,耕升 GeForce RTX 4060 踏雪+DLSS 3实现百帧玩光追

昨日,耕升正式发布了GeForce RTX 40系列的最新成员耕升 GeForce RTX 4060 追风。作为NVIDIA密切合作的核心AIC合作伙伴耕升,也将在今日为玩家带来了更多耕升GeForce RTX 40系列成员——耕升 GeForce RTX 4060 踏雪。作为耕升 GeForce RTX 4060系列中的双…

Axure教程—上传文件

本文介绍用Axure制作文件上传效果 预览 预览地址:https://6q4of2.axshare.com 功能 1、点击”文件上传“按钮,显示上传的文件 2、点击”删除“图片,显示提示”是否要删除“,点击”是“,删除数据,点击”否…

基于Java人力资源管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…