
在Go语言中,Goroutine是轻量级的并发执行单元,由Go运行时(runtime)负责调度。开发者常常期望通过go关键字启动的多个Goroutine能够“并排”或即时交错地执行。然而,在实际观察中,尤其是在默认配置下,可能会发现Goroutine的执行呈现出分块(chunked)而非细粒度交错的模式。这种现象揭示了Go调度器的工作原理以及GOMAXPROCS配置的关键作用。
考虑以下示例代码,它启动了两个Goroutine,每个Goroutine都在独立地计算斐波那契数列:
package main
import (
"fmt"
"runtime" // 引入runtime包
"time"
)
// fib 计算斐波那契数列的第n项,这是一个CPU密集型操作
func fib(n int) int {
if n <= 1 {
return n
}
return fib(n-1) + fib(n-2)
}
// worker 模拟一个CPU密集型任务
func worker(from string) {
for i := 0; i < 40; i++ {
// 模拟一些工作,例如计算斐波那契数列
result := fib(i)
fmt.Printf("%s fib( %d ): %d\n", from, i, result)
}
}
func main() {
// 默认情况下,Go 1.5+ 会将 GOMAXPROCS 设置为 CPU 核心数。
// 但为了演示单核行为,我们可以在此处显式设置为1
// 或者在运行程序时设置环境变量 GOMAXPROCS=1
// runtime.GOMAXPROCS(1) // 可以取消注释以强制单核调度
go worker("|||") // 启动第一个Goroutine
go worker("---") // 启动第二个Goroutine
// 等待Goroutine完成,或者通过其他机制控制程序退出
// 这里使用一个简单的等待输入来防止main Goroutine过早退出
fmt.Println("Press Enter to exit...")
var input string
fmt.Scanln(&input)
fmt.Println("Done.")
}
在某些环境下运行上述代码时(特别是当GOMAXPROCS被设置为1时),可能会观察到这样的输出模式:首先,一个Goroutine(例如---)会连续执行很长一段时间,完成其大部分甚至全部迭代;然后,另一个Goroutine(例如|||)才会接管并执行。只有在任务接近尾声时,才可能出现两个Goroutine交替执行的现象。这种行为与我们直觉中期望的随机、细粒度交错的“并行”执行有所不同。
Go运行时包含一个M:N调度器,它将多个Goroutine(M)映射到少量操作系统线程(N)上。这些操作系统线程再由操作系统的调度器映射到可用的CPU核心上。Go调度器的主要职责是管理Goroutine的生命周期,并在操作系统线程上高效地切换它们。
在默认情况下或当GOMAXPROCS设置为1时,Go调度器将所有Goroutine调度到一个操作系统线程上。这意味着,尽管有多个Goroutine,它们在任何给定时刻都只能在一个CPU核心上“并发”执行,即通过时间片轮转或协作式调度(例如,当Goroutine执行I/O操作或显式调用runtime.Gosched()时)来模拟并发。在这种单线程模型下,一个CPU密集型Goroutine可能会长时间占用CPU,直到它完成其当前任务块或Go调度器强制其让出CPU。
GOMAXPROCS是一个环境变量或可以通过runtime.GOMAXPROCS()函数设置的参数,它控制Go运行时可以使用的最大操作系统线程数。这些操作系统线程被称为“处理器”(Processor,P),每个P可以执行一个Go M(Machine,操作系统线程)上的Goroutine。
N世界
一分钟搭建会展元宇宙
138
查看详情
自Go 1.5版本起,GOMAXPROCS的默认值已更改为机器上的逻辑CPU核心数。这意味着在现代Go版本中,如果系统有多个核心,Goroutine默认就会被调度到多个核心上并行执行。然而,如果开发者显式地将GOMAXPROCS设置为1,或者在某些特殊环境中,仍可能观察到单核行为。
为了让Goroutine在多核CPU上实现更接近并行的执行,最直接的方法是确保GOMAXPROCS被设置为大于1的值,通常是系统的CPU核心数。
通过环境变量设置: 在运行Go程序之前,设置GOMAXPROCS环境变量。
export GOMAXPROCS=4 # 例如,设置为4个核心 go run your_program.go
或者在Windows上:
set GOMAXPROCS=4 go run your_program.go
通过 runtime.GOMAXPROCS() 函数设置: 在程序启动时,可以通过调用runtime.GOMAXPROCS()函数来设置。通常,建议将其设置为runtime.NumCPU()返回的值,以充分利用所有可用的CPU核心。
package main
import (
"fmt"
"runtime"
"time"
)
func fib(n int) int {
if n <= 1 {
return n
}
return fib(n-1) + fib(n-2)
}
func worker(from string
) {
for i := 0; i < 40; i++ {
result := fib(i)
fmt.Printf("%s fib( %d ): %d\n", from, i, result)
}
}
func main() {
// 设置 GOMAXPROCS 为可用的CPU核心数
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Printf("GOMAXPROCS set to: %d\n", runtime.GOMAXPROCS(0)) // 打印当前GOMAXPROCS值
go worker("|||")
go worker("---")
fmt.Println("Press Enter to exit...")
var input string
fmt.Scanln(&input)
fmt.Println("Done.")
}当GOMAXPROCS设置为系统CPU核心数时,再次运行上述代码,您会观察到两个Goroutine的输出更加频繁地交错出现,因为它们现在有机会在不同的CPU核心上同时执行。
Go语言的Goroutine和调度器提供了一种强大而高效的并发模型。理解GOMAXPROCS参数对于优化Go程序的性能和正确理解其并发行为至关重要。当观察到Goroutine执行呈现出分块而非细粒度交错时,通常是因为Go运行时被限制在单个操作系统线程上执行。通过合理配置GOMAXPROCS,我们可以充分利用多核CPU的优势,实现Goroutine的真正并行执行,从而提升CPU密集型应用的性能。
以上就是Go Goroutine调度与并发执行深度解析的详细内容,更多请关注其它相关文章!
相关文章:
Yandex官方入口网址 Yandex俄罗斯搜索引擎最新在线地址
Go语言HTML解析:利用Goquery精准获取指定元素内容
Win11怎么开启省电模式_Win11电池节电模式自动开启
铃兰之剑为这和平的世界希里技能组及加点推荐
C++如何打印当前代码行号与文件名_C++预定义宏FILE与LINE的使用
Spyder启动失败:字体文件权限拒绝错误解决方案
高德地图家和公司地址在哪设置 高德地图通勤路线设置方法【超详细】
怎样更改Windows系统的默认安装路径_避免C盘爆满的终极设置【技巧】
AO3官方可用镜像 Archive of Our Own网页版最新入口
支付宝如何设置安全保护_支付宝安全设置的全面教程
生成rdflib自定义SPARQL函数:参数匹配与实践指南
在J*a中如何在J*a中使用异常机制记录错误日志_异常日志实践经验
漫蛙MANWA漫画主页官方入口 漫蛙漫画最新在线阅读地址
HTML5原生日期选择器与jQuery UI:实现日期选择器的联动与程序化控制
晋江读书网页版在线登录 晋江读书电脑版官网
在WordPress中通过REST API访问受BasicAuth保护的站点内容
Yandex搜索引擎官方地址 俄罗斯网络世界的主要入口
12306选座怎么选到商务座_12306商务座选择与配置说明
在PHP脚本中通过SSHFS挂载远程文件系统的最佳实践与常见问题解决
J*aScript中向JSON对象添加新属性的正确姿势
Composer如何处理Git子模块(submodule)依赖_Composer与Git Submodule的对比与选择
漫蛙2网页版漫画入口 漫蛙漫画在线官方登录
微信群消息显示延迟如何解决 微信群消息刷新优化方法
印象笔记如何设提醒任务防漏执行_印象笔记设提醒任务防漏执行【任务提醒】
使用PHP DOM解析器高效提取HTML中特定标题及其紧邻段落
Pandas DataFrame 高效批量赋值:告别循环与笛卡尔积误区
QQ邮箱电脑版登录入口_QQ邮箱官方网站登录平台
Win11怎么关闭快速启动_Win11彻底关机设置教程
PHP表单隐藏域数据传递:常见问题与最佳实践
Spring Boot内嵌服务器与J*a EE全栈特性:选择与部署策略
PDO预处理语句中冒号的正确处理:区分SQL函数格式与命名占位符
c++如何实现一个简单的软件渲染器_c++从零开始的3D图形学
优化LangChain文档加载与ChromaDB集成:解决多文档处理与分块问题
Angular Material 垂直步进器:实现底部到顶部排序的教程
C#使用XPath查询节点时出错? 常见语法错误与调试技巧
J*a最大堆Heapify方法修复:索引计算与边界条件深度解析
Python大型XML文件高效流式解析教程
Web Components中自定义开关组件状态同步的常见陷阱与解决方案
AO3中文官网链接_AO3网页版稳定镜像站
TikTok搜索不到用户发布内容怎么办 TikTok用户内容搜索优化方法
win11开机启动修复循环怎么办 Win11无法进入系统高级启动解决方法【修复】
uc浏览器网页版极速入口 uc网页浏览器网页版流畅体验
如何高效处理PHP中的Excel数据导入导出?PortPHP/Spreadsheet助你轻松搞定!
实现全屏滚动与导航点:专业教程
Win10磁盘清理工具在哪 Win10打开并使用磁盘清理【教程】
C++ map遍历方法大全_C++ map迭代器使用总结
Lar*el Excel导入时生成自定义递增ID的策略与实践
c++项目目录结构应该如何组织_c++工程化项目结构规范
菜鸟取件码是什么怎么查 最全查询渠道汇总
Safari浏览器输入栏卡顿如何解决 Safari搜索建议与缓存清理