本文深入探讨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
# 循环
# 线程
相关文章:
专业网站制作服务公司,有哪些网站可以免费发布招聘信息?
建站主机数据库如何配置才能提升网站性能?
javascript中对象的定义、使用以及对象和原型链操作小结
建站之星手机一键生成:多端自适应+小程序开发快速建站指南
建站之星安装失败:服务器环境不兼容?
C++如何将C风格字符串(char*)转换为std::string?(代码示例)
北京制作网站的公司,北京铁路集团官方网站?
高防服务器租用首荐平台,企业级优惠套餐快速部署
如何用wdcp快速搭建高效网站?
网站制作网站,深圳做网站哪家比较好?
电商网站制作公司有哪些,1688网是什么意思?
网站建设设计制作营销公司南阳,如何策划设计和建设网站?
成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?
天津个人网站制作公司,天津网约车驾驶员从业资格证官网?
深圳网站制作平台,深圳市做网站好的公司有哪些?
如何挑选高效建站主机与优质域名?
建站之星如何通过成品分离优化网站效率?
湖北网站制作公司有哪些,湖北清能集团官网?
常州企业网站制作公司,全国继续教育网怎么登录?
专业网站制作企业网站,如何制作一个企业网站,建设网站的基本步骤有哪些?
寿县云建站:智能SEO优化与多行业模板快速上线指南
IOS倒计时设置UIButton标题title的抖动问题
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
如何在阿里云虚拟服务器快速搭建网站?
学校建站服务器如何选型才能满足性能需求?
山东云建站价格为何差异显著?
如何在橙子建站中快速调整背景颜色?
建站之星云端配置指南:模板选择与SEO优化一键生成
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
如何在阿里云完成域名注册与建站?
网站制作软件有哪些,制图软件有哪些?
免费制作小说封面的网站有哪些,怎么接网站批量的封面单?
东莞市网站制作公司有哪些,东莞找工作用什么网站好?
赚钱网站制作软件,建一个网站怎样才能赚钱?是如何盈利的?
建站三合一如何选?哪家性价比更高?
已有域名和空间如何搭建网站?
建站之星如何助力网站排名飙升?揭秘高效技巧
微网站制作教程,不会写代码,不会编程,怎么样建自己的网站?
建站之星代理费用多少?最新价格详情介绍
简历在线制作网站免费,免费下载个人简历的网站是哪些?
如何快速查询网站的真实建站时间?
建站之星2.7模板:企业网站建设与h5定制设计专题
建站之星Pro快速搭建教程:模板选择与功能配置指南
常州自助建站费用包含哪些项目?
国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?
如何在IIS中新建站点并配置端口与物理路径?
用v-html解决Vue.js渲染中html标签不被解析的问题
如何快速搭建高效WAP手机网站?
建站之星导航如何优化提升用户体验?
商务网站制作工程师,从哪几个方面把握电子商务网站主页和页面的特色设计?
*请认真填写需求信息,我们会在24小时内与您取得联系。