主框架
组件简述
M: machine的缩写。一个M代表一个内核线程,或称"工作线程"
P: processor的缩写。一个P代表执行一个Go代码片段所必需的资源(或称"上下文环境")
G: goroutine的缩写。一个G代表一个GO代码片段。前者是对后者的一种封装
组件功能简述
一个G的执行需要P和M的支持。一个M在与一个P关联之后,就形成了一个有效的G运行环境(内核线程+上下文环境)
每个P都会包含一个可运行的G的队列(runq)。该队列中的G会依次传递给与本地P关联的M,并获取运行时机
可以看到,M与KSE之间总是一对一的关系,一个M能且仅能代表一个内核线程
Go的运行时系统(runtime system)用M代表一个内核调度实体。M与KSE之间的关联非常稳固,一个M在其生命周期内,会且仅会与一个KSE产生关联
相比之下,M与P、P与G之间的关联是易变的,它们之间的关系会在实际调度的过程中改变
- 其中,M与P之间也总是一对一的,而P与G之间也会建立关联
- 因为一个G终归会由一个M来负责运行,它们之间的关联会由P来牵线
M
概述
一个M代表了一个内核线程。在大多数情况下,创建一个M,都是由于没有足够的M来关联P并运行其中可运行的G
不过,在运行时系统执行系统监控或垃圾回收等任务的时候,也会导致新M的创建
M的结构
g0: 表示一个特殊goroutine。这个goroutime是Go运行时系统在启动之初创建的,用于执行一些运行时任务
mstartfn: 表示M的起始函数,这个函数其实就是在编写go语句时携带的那个函数
curg: 表示存放当前M正在运行的那个G的指针
p: 表示与当前M相关联的那个P
nextp: 表示与当前M有潜在关联的P。把调度器将某个P赋给某个M的nextp字段操作,称为对M和P的预联。运行时系统有时候会把刚重新启动的M和已与它预联的那个P关联在一起,这也是nextp字段的主要作用
spinning: bool类型的,它用于表示这个M是否正在寻找可运行的G。在寻找过程中,M会处于自旋状态。Go运行时系统可以把一个M和一个G锁定在一起,一旦锁定,这个M就只能运行这个G,这个G也只能由该M运行
M的运行
基础的运行
M在创建之初,会被加入全局的M列表(runtime.allm)中。这时,它的起始函数和预联的P也会被设置
最后,运行时系统会为这个M专门创建一个新的内核线程并与之相关联。
如此一来,这个M就为执行G做好了准备。其中,起始函数仅当运行时候系统要用此M执行系统监控或垃圾回收等任务的时候才会被设置
这里的全局M列表其实并没有什么特殊的意义。运行时系统在需要的时候,会通过它获取所有M的信息。同时,它也可以防止M被当作垃圾回收掉
M的优化
在新M被创建之后,Go运行时系统会对它进行一番初始化,其中包括对自身所持的栈空间以及信号处理方面的初始化
在这个初始化工作都完成之后,该M的起始函数会执行。注意,如果这个起始函数代表的是系统监控任务,那么该M会一直执行下去,而不会继续后面的流程
否则,在起始函数执行完毕之后,当前M将会与那个预联的P完成关联,并准备执行其他任务。M会依次在多处寻找可运行的G并运行之。有了M,Go程序的并发运行基础才得以形成
M的空闲时的状态
运行时系统管辖的M(runtime.allm中的M)有时候也会被停止,比如在运行时系统执行垃圾回收任务过程中
运行时系统在停止M的时候,会把它放入调度器的空闲M列表(runtime.sched.midle)。这很重要,因为在需要一个未被使用的M时,运行时系统会先尝试从该列表中获取
M是否空闲,仅以它是否存在于调度器的空闲M列表中为依据
M的数量设置
单个Go程序所使用的M的最大数量是可以设置的。Go程序运行时候会启动一个引导程序,这个引导程序会为其运行建立必要的环境
在初始化调度器的时候,它会对M的最大数量进行初始设置,这个初始值是10000.也就是说,一个Go程序最多可以使用10000个M
- 这意味着,最多可以有10000个内核线程服务于当前的Go程序
可以调用标准库代码包runtime/debug中的SetMaxThreads函数,并提供新的M的最大数量
runtime/debug.SetMaxThreads函数在执行完成后,会把旧的M最大数量作为结果值返回
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » Go线程实现模型-主框架、M
发表评论 取消回复