首页 文章 Go语言基础 golang 获取g_Golang中Goroutine的调度流程
0
0
0
2

golang 获取g_Golang中Goroutine的调度流程

并发控制 流程控制 Goroutine g_Golang 流程 golang Golang

首先golang中协程golang是用户线程与系统线程的对应关系是多对多,既能利用多核cpu资源,也能尽可能减少上下文切换成本,代价是go需要实现复杂的goroutine调度机制。

N:1,所有用户线程对应1个系统线程,无法利用多核cpu;

1:1,1个用户线程对应一个系统线程,上下文切换成本高。

调度逻辑

四个结构体

M:Machine,操作系统线程。一个M被创建后会在P空闲队列中获取P进行绑定,未绑定则进入阻塞状态。

P:Process,调度器的核心处理器。通常表示执行上下文,P用于绑定M和G,由GOMAXPROCS指定最大数量,默认数量为cpu核心数。P可以跟多个M绑定,但同一时刻P只绑定在一个M上。

G:Goroutine,用户级线程。

(G0:调度函数的goroutine,go进程创建就会存在,负责调度工作。)

S:Scheduler,全局调度器,维护M、P和G队列,负责调度。

两个队列

本地队列:每个P维护一个本地队列,存放G,当前与P绑定的M若新生成G,会放入本地队列,当本地队列满时会拿出一半的本地队列中的G放入全局队列;

全局队列:存放本地队列溢出的G。调度过程中有1/61的概率检查全局队列,确保全局队列中的G也会被调度。

抢占式调度逻辑:

M绑定的P首先有1/61概率从全局队列获取G,60/61概率从本地队列获取G;

全局队列情况下如果没有获取到G,那么从本地队列获取G;

如果本地队列没有G,那么P从其他P的本地队列窃取G;

如果窃取不到G,那么从全局队列中获取一部分G到本地队列,获取n = min(len(GQ)/GOMAXPROCS + 1, len(GQ/2))个;

P获取到G后,绑定的M负责执行G,M必须是运行状态的线程,否则不会真正执行。

调度本质:调度器P将协程G合理地分配到系统线程M上执行。

如果当前的M执行的G调用syscall阻塞,P会与M进行分离,M负责执行阻塞的G,P带着队列中的G绑定到新的M中,继续执行其他G;使得虽然当前G进入阻塞,但并没有影响到P去执行其他G。

M执行的G阻塞操作返回后,由于没有了P,失去切换上下文执行后续逻辑的机会,因此尝试获取新的P去执行,如果获取不到P,M就把当前G放入全局队列等待调度,自己置于休眠状态。

窃取:如果当前M绑定的P中没有可执行的G,那么就会随机从其他P中拿取一般的G放入自己的队列,以提高资源利用率。

线程自旋

线程自旋相对于线程阻塞,表现为循环执行指定的逻辑,而不进入阻塞状态。在go的调度逻辑中,为了实现高性能的并发,如果全局队列和本地队列都为空,绑定P的M没有G可以执行,会进入自旋状态等待新的G,不会进入阻塞状态休眠,减少了M的上下文切换成本。

注意只有绑定了P的M会进入自旋状态,因此最多会有GOMAXPROCS个自旋线程,避免了浪费过多系统资源,其余未绑定的空闲M依然会进入休眠状态。

到此这篇关于“golang 获取g_Golang中Goroutine的调度流程”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持Go语言编程网!

相关文章

创建博客

开始创作
写作能提升自己能力,也能为他人分享知识。

在线教程

查看更多
  • Go入门指南

    Go入门指南

  • Go语言高级编程

    Go语言高级编程

  • Go Web 编程

    Go Web 编程

  • GO专家编程

    GO专家编程

  • Go语言四十二章经

    Go语言四十二章经

  • 数据结构和算法(Golang实现)

    数据结构和算法(Golang实现)

Go语言编程网

微信扫码关注订阅号


博客 资讯 教程 我的