zoukankan      html  css  js  c++  java
  • golang调度器原理与GMP模型设计思想

    ⼀、Golang“调度器”由来

    协程(co-routine), 引发的问题?(M:线程 N:协程)

    N:1  1.⽆法利⽤多个CPU  2.出现阻塞的瓶颈

    1:1   1.跟多线程/多进程模型⽆异  2.切换协程成本代价昂贵

    M:N  1.能够利⽤多核 2.过于依赖协程调度器的优化和算 法

    go-routine的优化?

    内存占⽤ ⼏KB ⼤量开辟

    灵活调度 切换成本低

    早期的Go的调度器 基本的全局Go队列和⽐较传统的 轮询利⽤多个thread去调度

    弊端

    1. 创建、销毁、调度G都需要每个M获取锁,这就形成了激烈的锁竞争。

    2. M转移G会造成延迟和额外的系统负载。

    3. 系统调⽤(CPU在M之间的切换)导致频繁的线程阻塞和取消阻塞操作增加了系统开销。

    ⼆、Goroutine调度器的GMP模型的设计思想

     GMP模型简介:

    G:goroutine 协程

    M:thread 内核线程

    P:processor 处理器

    全局队列 存放等待运⾏的G

    P的本地队列:  1存放等待运⾏的G 2数量限制 不超过256G 3 优先将新创建的G放在P的本地队 列中,如果满了会放在全局队列 中

    P列表: 1程序启动时创建 2最多有GOMAXPROCS个(可配置)

    M列表: 当前操作系统分配到当前Go程序的内核线程数

    P的数量: 1.环境变量$GOMAXPROCS  2.在程序中通过runtime.GOMAXPROCS() 来设置

    M的数量问题: 1.Go语⾔本身 是限定M的最⼤量是10000(忽略) 2.runtime/debug包中的SetMaxThreads函数来设置 3.有⼀个M阻塞,会创建⼀个新的M 4.如果有M空闲,那么就会回收或者睡眠

    调度器的设计策略

    1.复⽤线程 避免频繁的创建、销毁线程,⽽是对线程的复⽤。

    (1)work stealing机制 当本线程⽆可运⾏的G时,尝试 从其他线程绑定的P偷取G,⽽不 是销毁线程。

    (2)hand off机制 当本线程因为G进⾏系统调⽤阻 塞时,线程释放绑定的P,把P转 移给其他空闲的线程执⾏。

    2.利⽤并⾏ GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在多个CPU上同时运⾏。

    3.抢占 在coroutine中要等待⼀个协程主动让出CPU才执⾏下⼀个协程,在Go 中,⼀个goroutine最多占⽤CPU 10ms,防⽌其他goroutine被饿死

    4.全局G队列 当M执⾏work stealing从其他P偷不到G时,它可以从全局G队列获取G。

     三.“go func()” 经历了什么过程

    1、我们通过 go func()来创建⼀个goroutine;

    2、有两个存储G的队列,⼀个是局部调度器P的本地队列、⼀个是全局G队列。新创建的G会先 保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;

    3、G只能运⾏在M中,⼀个M必须持有⼀个P,M与P是1:1的关系。M会从P的本地队列弹出⼀个可执 ⾏状态的G来执⾏,如果P的本地队列为空,就会想其他的MP组合偷取⼀个可执⾏的G来执⾏;

    4、⼀个M调度G执⾏的过程是⼀个循环机制;

    5、当M执⾏某⼀个G时候如果发⽣了syscall或则其余阻塞操作,M会阻塞,如果当前有⼀些G在执⾏, runtime会把这个线程M从P中摘除(detach),然后再创建⼀个新的操作系统的线程(如果有空闲的线程可 ⽤就复⽤空闲线程)来服务于这个P;

    6、当M系统调⽤结束时候,这个G会尝试获取⼀个空闲的P执⾏,并放⼊到这个P的本地队列。如果获取不 到P,那么这个线程M变成休眠状态, 加⼊到空闲线程中,然后这个G会被放⼊全局队列中。

    四.调度器的⽣命周期

    M0 M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不需要在heap 上分配,M0负责执⾏初始化操作和启动第⼀个G, 在之后M0就和其他的M⼀样了。

    G0 G0是每次启动⼀个M都会第⼀个创建的gourtine,G0仅⽤于负责调度的G,G0不指向任何可执⾏的函数, 每个M都会有⼀个⾃⼰的G0。在调度或系统调⽤时会使⽤G0的栈空间, 全局变量的G0是M0的G0。

    五.可视化的GMP编程

    创建trace⽂件 f, err := os.Create("trace.out")

    启动trace trace.Start(f)

    停⽌trace trace.Stop()

    go build 并且运⾏之后,会得到⼀个trace.out⽂件

    go tool trace trace.out

    通过http://127.0.0.1:33479进⾏访问

    通过Debug trace查看GMP信息 

  • 相关阅读:
    [黑防VIP课程]汇编基础一日一学习2
    立即释放.net下的com组件
    WinExec,ShellExecute ,CreateProcess 区别
    .Net中如何操作IIS(原理篇)+实现类
    全用存储过程和全用SQL思考笔记
    C# 中的常用正则表达式总结
    .Net中窗体间传递值的一种方法
    [黑防VIP课程]汇编基础一日一学习1
    [黑防VIP课程]汇编基础一日一学习2
    浮点指令
  • 原文地址:https://www.cnblogs.com/peteremperor/p/13651940.html
Copyright © 2011-2022 走看看