• Goroutine的调度模型


    • 默认情况下,所有的goroutine都在同一个原生线程里跑,也就是只使用了一个CPU核。但是,通过runtime.GOMAXPROCS(4)设定,可以将goroutine调度到多个CPU上运行。
    • 在同一个原生线程里,若当前goroutine不发生阻塞,那么不会主动让出CPU给其他同一线程的goroutine的。在go程序启动时,会首先创建一个特殊的内核线程sysmom,负责监控和调度。

    三类对象:

    • M代表线程
    • P代表处理器,每一个运行的M(线程)都必须绑定一个P(处理器)
    • G代表goroutine,每次使用go关键字的时候,都会创建一个G对象

    图解:

    当前有两个P,各自绑定了一个M,每个P上挂了一个本地goroutine队列,也有一个全局goroutine队列。流程:

    1. 每次使用go关键字声明时,一个G对象被创建并加入到本地G队列或者全局G队列。
    2. 检查是否有空闲的P(处理器),若有那么创建一个M(若有正在sleep的M那么直接唤醒它)与其绑定,然后这个M循环执行goroutine任务。
    3. G任务执行的顺序是,先从本地队列中找。但若某个M(线程)发现本地队列为空,那么会从全局队列中截取goroutine来执行(一次性转移(全局队列的G个数/P个数))。如果全局队列也空,那么会随机从别的P那里截取【一半】的goroutine过来(偷窃任务),若所有的P的队列都为空,那么该M(线程)就会陷入sleep。

    三种调度点

    如果一个goroutine运行到一个“调度点”,上下文便从队列中取出一个goroutine,开始运行新的goroutine,下面是三种调度点:

    • 调用runtime.gosched函数。goroutine主动放弃CPU,该goroutine会被置为runnable状态,然后放入全局G队列,P继续执行下一个goroutine。主动行为,使用场景:一般发生在执行长任务又想其他goroutine得到执行机会时调用。
    • 调用runtime.park函数。goroutine进入wait状态,除非对其调用runtime.ready函数,否则该goroutine永远不刽得到执行。而P继续执行下一个G任务。使用场景:读写channel。一般是在某个条件如果得不到满足就不能继续运行下去时调用,当条件满足后需要使用runtime.ready唤醒它,类似于Java里的await和notify
    • 慢系统调用。将该处理器P上设置为syscall状态,然后对应的线程M进入系统调用阻塞等待。sysmom监控线程会定期扫描所有的P(处理器),若发现一个P处于syscall状态,就讲=将M(线程)和P(处理器)分离,并再分配一个M与这个P绑定,从而继续执行P本地队列中的其他goroutine。当之前阻塞的M从系统调用返回后,将其执行的goroutine放入全局G队列中,该M去sleep。
  • 相关阅读:
    Centos7下部署两套python版本并存
    运维监控系统之Open-Falcon
    Linux下如何查看系统启动时间和运行时间以及安装时间
    linux下使用FreeRDP 连接 Windows 远程桌面
    python3.6环境部署文档
    应用Fiddler对手机应用来抓包
    Highcharts 向下钻取饼图
    Highcharts 散点图
    IntelliJ IDEA2017 激活方法
    iterable 类型
  • 原文地址:https://www.cnblogs.com/maji233/p/11044913.html
Copyright © 2020-2023  润新知