我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Go语言中goroutine的调度原理是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Go语言中goroutine的调度原理是什么

Go语言中goroutine的调度原理是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

一、关于并发的基础知识

在讲goroutine的调度原理之前,有些与操作系统相关的知识,我们需要先知道,例如:

1.什么是并发?

并发:两个或两个以上的任务在一段时间内被执行。我们并不关心这些任务是否在同一时刻执行,我们只是知道,这些任务在这一段时间能能够都被执行,当然这一段时间可以很长,也可以很短。

2.并发的最小并发单位是什么?

进程是计算机资源分配最小的单位,是CPU分配资源的基本单位,具有独立的内存。

线程是计算机调度最小的单位,也是程序执行的最小单位,是在进程中的,一个进程往往会有一个到多个线程。

3.计算机是如何实现并发的?

计算机的分时调用是并发的根本,CPU通过快速的切换作业来执行不同的作业,基本的调度单位在执行的时候可以被阻塞掉,此时就会将CPU资源让出来,等到该调度单位再次被唤醒的时候,又可以使用CPU资源,而操作系统保证了整个的调度过程。

二、Goroutine的基础知识

除此之外,关于goroutine的调度原理,我们需要弄清楚下面几个问题。

1.goroutine是什么?

Goroutine:是Go里的一种轻量级线程——协程。

1)相对线程,协程的优势就在于它非常轻量级,进行上下文切换的代价非常的小。

2)对于一个goroutine ,每个结构体G中有一个sched的属性就是用来保存它上下文的。这样,goroutine 就可以很轻易的来回切换。

3)由于其上下文切换在用户态下发生,根本不必进入内核态,所以速度很快。而且只有当前goroutine 的 PC, SP等少量信息需要保存。

4)在Go语言中,每一个并发的执行单元为一个goroutine。

Go 语言中的goroutine并发, 采用的是CSP(communicating sequential processes)并发模型,讲究的是以通讯的方式来进行数据共享,是通过goroutine配合channel的方式来实现的。(备注:这部分知识后续单独整理一章。)

2.既然它是比线程还小的粒度,那么它与线程有什么关系?

Go语言的线程模型就是一种特殊的两级线程模型,如下所示:

两级线程模型的实现非常复杂,和内核级线程模型类似,一个进程中可以对应多个内核级线程,但是进程中的线程不和内核线程一一对应;这种线程模型会先创建多个内核级线程,然后用自身的用户级线程去对应创建的多个内核级线程,自身的用户级线程需要本身程序去调度,内核级的线程交给操作系统内核去调度。

三、Goroutine的调度策略

我们先来看下,Go线程实现了MPG模型:

S(Sched):结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。

M(Machine):一个M直接关联了一个内核线程。

P(processor):代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。G(Goroutine):其实本质上也是一种轻量级的线程。

它们的关系如下所示:

介绍:

一个M会关联两个东西,一个是内核线程,一个是可执行的进程。

一个上下文P会有两类Goroutine,一类是正在运行的,图中的蓝色G;一类是正在排队的,图中灰色G,这个会存储在该进程中的runqueue里面。

这里的上下文P的数量也表示的是Goroutinue运行的数量,一般设置为几个,机器中就会并发运行几个。当然这里P的数量是可以设置的,通过环境变量GOMAXPROCS的值,或者通过运行时调用函数runtime.GOMAXPROCS()进行设置,最大值是256。

有了上面的知识,我们知道了Goroutine的一些基本概念,但是我们还是不知道,Go的并发是如何调度的。而这一个话题,就需要我们将Goroutine的几种场景(创建、销毁和运行)做拆分。

1.在执行go语句之前,我们看下程序都做了哪些准备,也就是程序的初始化启动流程是什么样子的?

上面的代码,有三个点非常关键,分别是runtime.schedinit,runtime.main,runtime.mstart

Step1: runtime.schedinit:这一步是调度器的初始化操作,它会设置GOMAXPROCS的大小,这里的大小不能超过它的上限256,并创建设置好对应数量的P,当然这些P都处于闲置状态;然后,将这些创建好的P都存放到sched中pidle所关联的闲置列表中。

Step2: 程序会继续执行runtime.newproc来创建程序的第一个goroutine,而这个goroutine会执行runtime.main也就是我们看到的main函数,在这之后main会主动创建一个内核线程M,这个M只用来做系统监控用,这个内核线程与程序中goroutinue的调度有关系。

Step3:在runtime.mstart之后,程序就开始执行了,如果后续需要创建goroutine,就会调用go语句来创建。

2.goroutine创建流程是什么样子的?

在调用go func()的时候,会调用runtime.newproc来创建一个goroutine,这个goroutine会新建一个自己的栈空间,同时在G的sched中维护栈地址与程序计数器这些信息(备注:这些数据在goroutine被调度的时候会被用到。准确的说该goroutine在放弃cpu之后,下一次在重新获取cpu的时候,这些信息会被重新加载到cpu的寄存器中。)

创建好的这个goroutine会被放到,它所对应的内核线程M所使用的上下文P中的runqueue中。等待调度器来决定何时取出该goroutine并执行,通常调度是按时间顺序被调度的,这个队列是一个先进先出的队列。

3.新建的这些goroutine是如何被调度的呢?

goroutine在创建好了之后,调度器会决定何时执行这个goroutine,这个过程就叫做调度。

新建好的goroutine,最开始都会存储在某一个线程M,所关联的上下文P的runqueue中,但是在后续的调度中,有些goroutine因为调用了runtime.gosched,会被放到全局队列中。

线程M的选择过程,按照下面的顺序执行:

1.从M对应的P中的runqueue中取出goroutine,来执行,没有的话,执行2。

2.从全局队列里面尝试取出一个goroutine来执行,有的话,执行!没有的话,执行3。

3.从其他的线程M的P中,偷出一些goroutine来执行,偷失败了,执行4。(备注:这里偷的话,一偷就偷一半,使用的算法叫做work stealing。)

4.线程M发现无事可做,就去休息了,也就是线程的sleep,它等待被唤醒。

4.运行中的goroutine是怎么停止的呢?一旦被停止了的话,那排队在它后面的goutinue该怎么办?

讲完了goroutine的调度之后,我们便要考虑一个问题,正在被执行的goroutine何时停止,停止了之后会发生什么?而挂在M对应的P后面的runqueue中的goroutine该怎么办?

情况1:runtime·park

当调用了runtime·park函数之后,goroutine会被设置成waiting状态,线程M会放弃它自身关联的上下文P,而系统会分配一个新的线程M1来接管这个上下文P,(备注:当然这里面的M1也有可能是本来就创建好的,处于闲置状态中的)。

原来的线程M0则会与上下文断开连接,M0因为无事可做,就去sleep了,等待下次被唤醒。如下图所示:

channel的读写操作,定时器中,网络poll等都有可能park goroutine。

情况2:runtime·gosched

调用runtime·gosched函数也可以让当前goroutine放弃cpu,这种情况下会将goroutine设置成runnable,放置到全局队列中。备注:这个也就是为什么全局变量的queue里面会有goroutine的原因。

5.goroutine被唤醒之后,会做什么?

goroutine处于waiting状态的话,在调用runtime·ready函数之后,会被唤醒,唤醒的goroutine会被重新放到,M对应的上下文所对应的runqueue中,等待被调度。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Go语言中goroutine的调度原理是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Go语言调度的本质是什么

本篇内容介绍了“Go语言调度的本质是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先抛出本文的结论:Go 调度的本质是一个生产-消费流
2023-06-15

go协程调度的原理是什么

Go协程的调度原理是基于M:N的模型,其中M代表操作系统的线程,N代表Go协程。Go运行时系统会创建一定数量的操作系统线程,每个线程被称为M,用于执行Go协程。这些M线程会在需要的时候自动创建和销毁,以适应不同的负载。Go运行时系统还会维
2023-10-23

go协程调度原理是什么

Go协程调度的原理是基于M:N调度模型。其中,M代表操作系统的线程(Machine),N代表Go语言的协程(Goroutine)。在启动时,Go程序会创建一组操作系统的线程(M),每个线程都维护着一个协程队列。当一个协程需要执行时,调度器会
2023-10-07

GO语言中Chan的实现原理是什么

今天小编给大家分享一下GO语言中Chan的实现原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。chan 是什么是一种
2023-07-05

GO语言中defer实现原理是什么

这篇文章主要介绍“GO语言中defer实现原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“GO语言中defer实现原理是什么”文章能帮助大家解决问题。defer 是什么咱们一起来看看 def
2023-07-05

Go并发编程中goroutine的管理与调度

go 语言中的 goroutine 可通过以下方式管理:1. 创建 goroutine:使用 "go" 关键字。2. 等待 goroutine 退出:使用 waitgroup。3. 取消 goroutine:使用 context.conte
Go并发编程中goroutine的管理与调度
2024-05-12

Go语言中并发goroutine底层原理的示例分析

小编给大家分享一下Go语言中并发goroutine底层原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、基本概念①并发、并行区分1.概念并发:同一时间段内一个对象执行多个任务,充分利用时间并行:同一时刻,多个
2023-06-29

React调度的原理是什么

这篇文章主要介绍“React调度的原理是什么”,在日常操作中,相信很多人在React调度的原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”React调度的原理是什么”的疑惑有所帮助!接下来,请跟着小编
2023-07-02

C语言函数的调用原理是什么

C语言函数的调用原理是通过栈来实现的。当一个函数被调用时,系统会为该函数分配一块内存空间,这块空间被称为栈帧。栈帧包含了函数的参数、局部变量以及其他与函数执行相关的信息。函数调用时,系统将函数的返回地址(即调用函数后继续执行的位置)压入栈中
2023-09-04

go语言中slice,map,channl底层原理是什么

今天小编给大家分享一下go语言中slice,map,channl底层原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
2023-06-30

Go语言中为什么要有GMP调度模型

今天小编给大家分享一下Go语言中为什么要有GMP调度模型的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。GMP调度模型是Go的
2023-07-06

go语言调用约定多返回值实现原理是什么

这篇文章主要介绍了go语言调用约定多返回值实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言调用约定多返回值实现原理是什么文章都会有所收获,下面我们一起来看看吧。go简单代码反汇编用简单的代码
2023-06-30

Go语言中的Goroutine和channel怎么使用

这篇“Go语言中的Goroutine和channel怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go语言中的Gor
2023-07-05

Go语言的make和new实现原理是什么

这篇文章主要介绍“Go语言的make和new实现原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言的make和new实现原理是什么”文章能帮助大家解决问题。概述虽然 make 和 ne
2023-07-05

编程热搜

目录