golang调度模型

栏目: Go · 发布时间: 5年前

内容简介:关键点:每一个用户线程绑定一个实际的内核线程,而线程的调度则完全交付给操作系统内核去做,应用程序对线程的创建、终止以及同步都基于内核提供的系统调用来完成用户级线程模型

线程模型

内核级线程模型(KSE(Kernel Scheduling Entity))

关键点: 完全靠操作系统调度

每一个用户线程绑定一个实际的内核线程,而线程的调度则完全交付给操作系统内核去做,应用程序对线程的创建、终止以及同步都基于内核提供的系统调用来完成

用户级线程模型

关键点: 完全靠自己调度

用户线程与内核线程KSE是多对一(N : 1)的映射模型,多个用户线程的一般从属于 单个进程 的调度是由用户自己的线程库来完成,线程的创建、销毁以及多线程之间的协调等操作都是由用户自己的线程库来负责而无须借助系统调用来实现。操作系统只知道用户进程而对其中的线程是无感知的,内核的所有调度都是基于用户进程。

两级(混合型)线程模型

关键点: 自身调度与系统调度协同工作

用户线程与内核KSE是多对多(N : M)的映射模型:

首先,区别于用户级线程模型,两级线程模型中的一个进程可以与多个内核线程KSE关联,于是进程内的多个线程可以绑定不同的KSE,这点和内核级线程模型相似;

其次,又区别于内核级线程模型,它的进程里的所有线程并不与KSE一一绑定,而是可以动态绑定不同KSE, 当某个KSE因为其绑定的线程的阻塞操作被内核调度出CPU时,其关联的进程中其余用户线程可以重新与其他KSE绑定运行

GPM模型

基本概念

OS线程抽象,代表着真正执行计算的资源, 每一个 goroutine 实际上就是在 M 中执行, M 的数量目前最多 10000 个.

M 并不保存 G 的状态, 与 G 本身并没有关系, 所以 G 可以在不同的 M 执行

分配程序执行的上下文环境, 数量 <= 内核数量, 即同时能够并行执行的 G 的数量,相对于 G 而言, P 的角色相当于CPU.

程序代码中的每一次使用关键字 go 执行函数其实都生成了一个 G ,并将之加入到本地的 G 队列中, 之后 M 会生成 G 执行的上下文也就是绑定 P 来执行函数.

G 维护者goroutine需要的栈、程序计数器以及它所在的M等信息。

  • Seched

代表着一个调度器 它维护有存储空闲的 M 队列和空闲的 P 队列,可运行的 G 队列,自由的 G 队列以及调度器的一些状态信息等。

模型调度

golang调度模型

1. P 如何获得 G
调度器 Seched 生成一个 M , 然后 M 需要持有(绑定)一个 P ,接着 M 会启动一个OS线程,循环让 P 会首先从自己的本地队列(Local Quequ)中取可执行(Runnable)的 G 执行, 如果本地队列中没有, 则会从全局队列(Globle Queue)中取 G , 如果还没有, 则会从其他的 P 的本地队列中取一半的队列放入自己本地队列之中

2. M 执行函数遇到阻塞,如何处理

实际代码执行中可能存在下面的问题,导致程序阻塞

blocking syscall (for example opening a file)
network input
channel operations
primitives in the sync package

主要可归为两类

  • 用户态阻塞/唤醒

goroutine 因为channel操作或者network I/O而阻塞时(实际上golang已经用netpoller实现了goroutine网络I/O阻塞不会导致M被阻塞,仅阻塞G,这里仅仅是举个栗子),对应的G会被放置到某个 wait 队列(如channel的waitq),该G的状态由 _Gruning 变为 _Gwaitting ,而M会跳过该G尝试获取并执行下一个G,如果此时没有runnable的G供M运行,那么M将解绑P,并进入 sleep 状态;当阻塞的G被另一端的G2唤醒时(比如channel的可读/写通知),G被标记为runnable,尝试加入G2所在P的runnext,然后再是P的Local队列和Global队列。

  • 系统调用阻塞

当G被阻塞在某个系统调用上时,此时G会阻塞在 _Gsyscall 状态,M也处于 block on syscall 状态,此时的M可被抢占调度:执行该G的M会与P解绑,而P则尝试与其它 idle 的M绑定,继续执行其它G。如果没有其它 idle 的M,但P的Local队列中仍然有G需要执行,则创建一个新的M;当系统调用完成后,G会重新尝试获取一个 idle 的P进入它的Local队列恢复执行,如果没有idle的P,G会被标记为runnable加入到Global队列。

调度使用了名叫 work stealing 的算法, 这种算法适用场景是任务之间的耗时相差比较大,即有的任务很耗时,有的任务很快完成,用这种用算法很合适;如果任务的耗时很平均则不适合,因为窃取任务也是需要抢占锁的,会造成额外的消耗。

参考文档


以上所述就是小编给大家介绍的《golang调度模型》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python 3面向对象编程

Python 3面向对象编程

[加]Dusty Phillips(达斯帝•菲利普斯) / 肖鹏、常贺、石琳 / 电子工业出版社 / 2015-6 / 79.00元

Python 是一种面向对象的解释型语言,面向对象是其非常重要的特性。《Python 3面向对象编程》通过Python 的数据结构、语法、设计模式,从简单到复杂,从初级到高级,一步步通过例子来展示了Python 中面向对象的概念和原则。 《Python 3面向对象编程》不是Python 的入门书籍,适合具有Python 基础经验的开发人员阅读。如果你拥有其他面向对象语言的经验,你会更容易理解......一起来看看 《Python 3面向对象编程》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换