GO的协程调度模型

本文详细介绍了Go语言的协程调度模型MPG,包括传统的多线程模型,以及Go中的M(工作线程)、P(处理器)和G(协程)的概念。Go的协程调度模型是一种特殊的两级线程模型,通过goroutine和channel实现并发。M直接关联内核线程,P代表逻辑处理器,G是执行单位,三者共同协作完成协程调度。G的状态和生命周期,以及P和M的状态转换和管理机制也在文中进行了阐述。
摘要由CSDN通过智能技术生成

一、常见的多线程模型

1、用户级线程模型
此时,用户线程和KSE(可以认为是内核线程)是M:1的关系,这m个用户级线程“共享”(对应)同一个内核线程,所以这m个用户级线程必须要互斥使用这1个内核线程,所以需要用户自己来管理调度这m个用户线程
在这里插入图片描述
2、内核级线程模型
此时用户线程和内核线程是1:1的关系,此时用户线程间的调度均可以交由内核来管理
在这里插入图片描述
两级线程模型
用户线程和内核线程是m:n也就是多对多的关系,一个用户线程可以对应多个内核线程,1个内核线程也可以对应多个用户线程。此时用户线程需由用户自己来管理,内核线程由内核来进行管理。go的协程调度模型MPG就是一种特殊的两级线程模型
在这里插入图片描述
go的协程调度模型MPG
在这里插入图片描述

二、传统线程模型

普通的线程并发模型,就是像Java、C++、或者Python,线程间通信都是通过共享内存的方式来进行的。非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问这些数据结构,然后基于里面的数据块,并发执行相关操作。因此,在很多时候,衍生出一种方便操作的数据结构,叫做“线程安全的数据结构”。例如Java提供的包”java.util.concurrent”中的数据结构。Go的sync包也提供了对传统的线程并发模型的支持,但Go本身更推崇CSP(communicating sequential processes)的并发模型(在go中CSP并发模型的实现叫MPG模型),通过goroutine和channel来组合实现

  • goroutine 是Go语言中并发的执行单位。即为go中的一个协程
  • channel是Go语言中各个并发执行单位(goroutine)之间的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,类似于Linux中的pipe管道

三、go中的协程调度模型 MPG

简单来说,go的协程调度模型为:

在这里插入图片描述
在这里插入图片描述

  • M指的是Machine,即工作线程,一个M直接关联了一个内核线程(KSE:内核调度实体),可以直接理解为M就是内核线程。M的数量m表示在程序执行的任意时刻底层最多有m个内核线程来执行我们的goroutine(也就是go语句)
  • P指的是processor,即逻辑处理器,表示M所需的上下文环境(或运行资源)。每个P维护一个p_Local任务队列(本地任务队列),由启动时环境变量 $GOMAXPROCS 或者是由runtime.GOMAXPROCS()决定,注意GOMAXPROCS的值不会影响M的数量。P的数量决定了程序的并行度,这意味着在程序执行的任意时刻,最多都只有$GOMAXPROCS个goroutine在同时运行!!!。
  • G指的是Goroutine,其内部不仅封装了我们用Go实现的协程(即用go关键字修饰的方法),还用于协程进行上下文切换时保存该协程的硬件上下文(相关栈信息,比如pc,sp(堆栈寄存器)等)

当一段go代码(被go关键字修饰的代码)想要运行时,其必须先与一个G相绑定,然后这个G想要运行,必须与一个逻辑处理器P和一个工作线程M相绑定。

G

  • goroutine:go中的协程也可以认为是一种受管理的轻量线程,goroutine使用go关键词创建(即为go语句)。
  • G的内部不仅封装了我们用Go实现的协程(即用go关键字修饰的方法),还用于协程进行上下文切换时保存该协程的硬件上下文(相关栈信息,比如pc,sp(堆栈寄存器)等)
    【注意】:跟Java类似,main()函数即为程序的主入口,其本质也就相当于开启了一个主线程,当然在go中就相当于开启了一个协程goroutine,所以下面那段代码相当于开启了两个协程,一个是主协程main,另一个是用于执行func1的协程
func main(){
   go func1(){
    .....
   }
}

G的源码位于src/runtime/runtime2.go,下面我们只摘取几个重要属性

type g struct {
	
	stack       stack   //当前G使用的栈空间, 有lo和hi两个成员
	
	stackguard0 uintptr //检查栈空间是否足够的值, 低于这个值会扩张栈, 0是go代码使用的
	
	stackguard1 uintptr //检查栈空间是否足够的值, 低于这个值会扩张栈, 0是原生代码(native code)使用的
	
	m            *m      // 当前G运行在的M
	
	sched        gobuf   //用于在进行上下文切换时保存当前上下文信息,gobuf也是一
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值