动态栈操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine 的初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建数万个 goroutine 也是可能的 。并且 goroutine 的栈不是固定的,可以根据需要动态地增大或缩小,Go 的 runtime 会自动为 goroutine 分配合适的栈空间 。
goroutine调度操作系统内核在调度时会挂起当前正在执行的线程并将寄存器中的内容保存到内存中,然后选出接下来要执行的线程并从内存中恢复该线程的寄存器信息,然后恢复执行该线程的现场并开始执行线程 。从一个线程切换到另一个线程需要完整的上下文切换 。因为可能需要多次内存访问,所以这个切换上下文的操作开销较大,会增加运行的cpu周期 。
区别于操作系统内核调度操作系统线程,goroutine 的调度是Go语言运行时(runtime)层面的实现,是完全由 Go 语言本身实现的一套调度系统——go scheduler 。它的作用是按照一定的规则将所有的 goroutine 调度到操作系统线程上执行 。
【go GMP】在经历数个版本的迭代之后,目前 Go 语言的调度器采用的是 GPM
调度模型 。
文章插图
- G:表示 goroutine,每执行一次
go f()
就创建一个 G,包含要执行的函数和上下文信息 。 - 全局队列(Global Queue):存放等待运行的 G 。
- P:表示 goroutine 执行所需的资源,最多有 GOMAXPROCS 个 。
- P 的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个 。新建 G 时,G 优先加入到 P 的本地队列,如果本地队列满了会批量移动部分 G 到全局队列 。
- M:线程想运行任务就得获取 P,从 P 的本地队列获取 G,当 P 的本地队列为空时,M 也会尝试从全局队列或其他 P 的本地队列获取 G 。M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去 。
- Goroutine 调度器和操作系统调度器是通过 M 结合起来的,每个 M 都代表了1个内核线程,操作系统调度器负责把内核线程分配到 CPU 的核上执行 。
GOMAXPROCSGo运行时的调度器使用
GOMAXPROCS
参数来确定需要使用多少个 OS 线程来同时执行 Go 代码 。默认值是机器上的 CPU 核心数 。例如在一个 8 核心的机器上,GOMAXPROCS 默认为 8 。Go语言中可以通过runtime.GOMAXPROCS
函数设置当前程序并发时占用的 CPU逻辑核心数 。(Go1.5版本之前,默认使用的是单核心执行 。Go1.5 版本之后,默认使用全部的CPU 逻辑核心数 。)经验总结扩展阅读
- 3 onps栈使用说明——tcp、udp通讯测试
- Java安全之动态加载字节码
- 2 onps栈使用说明——ping、域名解析等网络工具测试
- Vue3实现动态导入Excel表格数据
- Go实现栈与队列基本操作
- 定位java程序中占用cpu最高的线程堆栈信息
- 手机动态彩铃是怎么设置的(自己手机的彩铃能设置吗)
- 1 onps栈使用说明——API接口手册
- Flask框架:运用Ajax轮询动态绘图
- 小白也会 HTML 动态爱心-详细教程