本文深入探讨go语言中扇入(fan-in)并发模式在实际运行时可能出现的顺序执行现象。我们将揭示go调度器与`gomaxprocs`参数的内在机制,解释为何多协程在默认设置下可能无法充分并行。通过配置`runtime.gomaxprocs`来利用多核cpu,读者将学会如何正确实现并观察真正的并发执行,从而优化go应用程序的性能。
Go语言以其强大的并发原语而闻名,其中“扇入”(Fan-In)模式是一种常见的并发模式,用于将多个并发源的输出合并到一个单一的通道中。这种模式允许我们从不同的服务或协程中收集数据,并以统一的方式进行处理,而无需关心数据来源于哪个具体的并发实体。
考虑一个简单的场景,我们有两个“无聊”的服务,它们各自以随机间隔生成消息。我们希望将这两个服务的输出合并到一个通道中,并按消息到达的顺序进行处理。以下是实现这一模式的典型Go代码结构:
package main
import (
"fmt"
"math/rand"
"time"
"runtime" // 引入runtime包
)
// boring 函数模拟一个持续生成消息的服务
func boring(msg string) <-chan string {
c := make(chan string)
go func() { // 在独立的goroutine中运行
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i) // 发送消息到通道
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) // 随机暂停
}
}()
return c
}
// fanIn 函数实现扇入模式,将两个输入通道的输出合并到一个通道
func fanIn(in1, in2 <-chan string) <-chan string {
c := make(chan string)
go func() { // goroutine 1: 从in1读取并写入c
for {
c <- <-in1
}
}()
go func() { // goroutine 2: 从in2读取并写入c
for {
c <- <-in2
}
}()
return c
}
func main() {
// 在main函数中调用fanIn来合并两个boring服务的输出
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You're both boring: I'm leaving")
}这段代码创建了两个boring协程,它们各自向自己的通道发送消息。fanIn函数又创建了两个协程来从这两个通道读取数据,并将它们“扇入”到一个公共通道c中。直观上,我们期望从c中读取到的消息是Joe和Ann交替出现,或者至少是随机顺序,因为它们都在独立的协程中运行,并且有随机的延迟。
然而,在某些运行环境下,上述代码的输出可能并非预期的随机或交替,而是呈现出高度的确定性顺序,例如:
Joe 0 Ann 0 Joe 1 Ann 1 Joe 2 Ann 2 ...
这种现象会让开发者感到困惑:明明启动了多个协程,为何输出却如此顺序,仿佛它们是在串行执行?这似乎与Go语言提倡的并发模型相悖。
要理解这种现象,我们需要深入了解Go语言的运行时调度器以及GOMAXPROCS参数的作用。
Go调度器是Go运行时的一个核心组件,负责将Go协程(goroutines)调度到操作系统线程(OS threads)上执行。Go协程是轻量级的,由Go运行时管理,而不是直接由操作系统管理。一个Go程序可以创建成千上万个协程,而这些协程最终会复用数量有限的操作系统线程。
GOMAXPROCS 参数决定了Go运行时可以同时使用的操作系统线程的最大数量。这些线程被称为“处理器”(Processor,P),每个P可以运行一个M(Machine,操作系统线程),M又可以运行一个G(Goroutine)。
为了让Go程序能够充分利用多核CPU,实现真正的并行执行,我们需要将 GOMAXPROCS 设置为一个大于1的值,通常是机器的CPU核心数。
解决方案:
使用 runtime.GOMAXPROCS 函数: 在程序启动时,通过调用 runtime.GOMAXPROCS(runtime.NumCPU()) 来设置 GOMAXPROCS 的值为当前机器的CPU核心数。这是最常见且推荐的做法,因为它能够使程序在不同机器上自动适应其硬件配置。
package main
import (
"fmt"
"math/rand"
"runtime" // 引入runtime包
"time"
)
// boring 和 fanIn 函数与之前相同
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
func fanIn(in1, in2 <-chan string) <-chan string {
c := make(chan string)
go func() { for { c <- <-in1 } }()
go func() { for { c <- <-in2 } }()
return c
}
func main() {
// 关键更改:设置GOMAXPROCS为CPU核心数
fmt.Println("NumCPU:", runtime.NumCPU())
runtime.GOMAXPROCS(runtime.NumCPU())
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You're both boring: I'm leaving")
}通过添加 runtime.GOMAXPROCS(runtime.NumCPU()),Go运行时现在可以启动与CPU核心数相同数量的OS线程来执行协程。这将允许Joe和Ann的boring协程以及fanIn内部的读取协程在不同的OS线程上真正并行运行,从而产生非确定性的交错输出。
设置 GOMAXPROCS 环境变量: 在运行Go程序之前,可以通过设置 GOMAXPROCS 环境变量来指定其值。 例如,在Linux/macOS上:
GOMAXPROCS=4 go run your_program.go
或者在Windows上:
set GOMAXPROCS=4 go run your_program.go
这种方式通常用于测试或临时调整,但在生产环境中,使用 runtime.GOMAXPROCS 函数更为灵活和推荐。
GOMAXPROCS=1 的情况下,如果循环次数足够大(例如,从10增加到40或更多),并且每个任务内部包含 time.Sleep 等可能导致协程让出CPU的操作,调度器仍然可能在不同协程之间切换,从而观察到非顺序的输出。这是因为 time.Sleep 会阻塞当前协程,给其他协程执行的机会。然而,这并非真正的并行,而是并发调度策略在起作用。设置 GOMAXPROCS > 1 才能确保真正的并行执行。Go语言的扇入(Fan-In)并发模式是构建响应式、高效应用程序的强大工具。然而,要充分发挥其并行潜力,理解Go调度器和 GOMAXPROCS 参数至关重要。当观察到多协程应用呈现顺序执行时,这通常是 GOMAXPROCS 设置为 1 的信号。通过在程序启动时显式调用 runtime.GOMAXPROCS(runtime.NumCPU()) 或设置 GOMAXPROCS 环境变量,我们可以指示Go运行时利用所有可用的CPU核心,从而实现真正的并行执行,并观察到协程之间非确定性的交错行为。这不仅能解决看似“顺序”的问题,更能确保Go应用程序在多核处理器上获得最佳性能。
# linux
# go
# windows
# 操作系统
# 处理器
# go语言
# 工具
# mac
# ai
# macos
# 环境变量
# win
# cos
# 循环
# 线程
相关文章:
C++时间戳转换成日期时间的步骤和示例代码
建站之星24小时客服电话如何获取?
手机网站制作与建设方案,手机网站如何建设?
如何选择高效便捷的WAP商城建站系统?
高端建站三要素:定制模板、企业官网与响应式设计优化
免费ppt制作网站,有没有值得推荐的免费PPT网站?
如何快速生成橙子建站落地页链接?
高防服务器:AI智能防御DDoS攻击与数据安全保障
开心动漫网站制作软件下载,十分开心动画为何停播?
如何设置并定期更换建站之星安全管理员密码?
全景视频制作网站有哪些,全景图怎么做成网页?
Avalonia如何实现跨窗口通信 Avalonia窗口间数据传递
中山网站制作网页,中山新生登记系统登记流程?
如何在景安服务器上快速搭建个人网站?
如何通过远程VPS快速搭建个人网站?
建站上市公司网站建设方案与SEO优化服务定制指南
制作宣传网站的软件,小红书可以宣传网站吗?
如何在搬瓦工VPS快速搭建网站?
香港服务器部署网站为何提示未备案?
建站之星如何防范黑客攻击与数据泄露?
如何做网站制作流程,*游戏网站怎么搭建?
如何确保FTP站点访问权限与数据传输安全?
制作网站的网址是什么,请问后缀为.com和.com.cn还有.cn的这三种网站是分别是什么类型的网站?
Swift中循环语句中的转移语句 break 和 continue
建站org新手必看:2024最新搭建流程与模板选择技巧
如何快速生成高效建站系统源代码?
Python路径拼接规范_跨平台处理说明【指导】
如何在IIS中配置站点IP、端口及主机头?
宝塔面板创建网站无法访问?如何快速排查修复?
如何使用Golang table-driven基准测试_多组数据测量函数效率
南京网站制作费用,南京远驱官方网站?
ppt在线制作免费网站推荐,有什么下载免费的ppt模板网站?
,交易猫的商品怎么发布到网站上去?
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
教学网站制作软件,学习*后期制作的网站有哪些?
免费网站制作appp,免费制作app哪个平台好?
如何通过WDCP绑定主域名及创建子域名站点?
如何在云主机上快速搭建多站点网站?
微信小程序 五星评分(包括半颗星评分)实例代码
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
Swift中switch语句区间和元组模式匹配
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
山东网站制作公司有哪些,山东大源集团官网?
jQuery 常见小例汇总
如何在阿里云域名上完成建站全流程?
python的本地网站制作,如何创建本地站点?
如何选择PHP开源工具快速搭建网站?
建站之星CMS五站合一模板配置与SEO优化指南
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
制作网站公司那家好,网络公司是做什么的?
*请认真填写需求信息,我们会在24小时内与您取得联系。