问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Golang无限开启Goroutine?该如何限定Goroutine数量?

发布网友 发布时间:2024-10-04 19:29

我来回答

1个回答

热心网友 时间:2024-10-04 23:37

如果不控制Goroutine的数量会出什么问题?

首先我们都知道Goroutine具备以下两个特点:

体积轻量(占内存小,一个2kb左右)

优秀的GMP调度(详见:图解Golang的GMP原理与调度流程)

那么goroutine是否真的可以无限制的开启呢?如果做一个服务器或者一些高业务的场景,能否随意的开启goroutine并且没有控制控制其生命周期呢?让这些goroutine自生自灭,相信大都数人都会这么想:毕竟有强大的GC和优秀的调度算法支撑,应该可以无限的开启吧。

我们先看下面一个例子:

demo1.go

packagemainimport("fmt""math""runtime")funcmain(){//模拟用户需求业务的数量task_cnt:=math.MaxInt64fori:=0;i<task_cnt;i++{gofunc(iint){//...dosomebusi...fmt.Println("gofunc",i,"goroutinecount=",runtime.NumGoroutine())}(i)}}

结果最后被操作系统以kill信号,强制终结了该进程。

signal:killed

可以看出,我们如果迅速的开启goroutine(不控制并发的goroutine数量)的话,会在短时间内占据操作系统的资源(CPU、内存、文件描述符等)。

CPU使用率浮动上涨

Memory占用不断上涨

主进程崩溃(被杀掉了)

这些资源实际上是所有用户态程序共享的资源,所以大批的goroutine开启最终引发的问题不仅仅是自身,还会影响到其他运行的程序。

综上所述,所以我们在平时开发中编写代码的时候,需要注意代码中开启的goroutine数量,以及开启的goroutine是否可以控制,必须要重视的问题。

控制goroutine的几种方法方法一:用有buffer的channel来限制packagemainimport("fmt""math""runtime")//模拟执行业务的goroutinefuncdoBusiness(chchanbool,iint){fmt.Println("gofunc:",i,"goroutinecount:",runtime.NumGoroutine())<-ch}funcmain(){max_cnt:=math.MaxInt64//max_cnt:=10fmt.Println(max_cnt)ch:=make(chanbool,3)fori:=0;i<max_cnt;i++{ch<-truegodoBusiness(ch,i)}}

结果:

...gofunc352283goroutinecount=4gofunc352284goroutinecount=4gofunc352285goroutinecount=4gofunc352286goroutinecount=4gofunc352287goroutinecount=4gofunc352288goroutinecount=4gofunc352289goroutinecount=4gofunc352290goroutinecount=4gofunc352291goroutinecount=4gofunc352292goroutinecount=4gofunc352293goroutinecount=4gofunc352294goroutinecount=4gofunc352295goroutinecount=4gofunc352296goroutinecount=4gofunc352297goroutinecount=4gofunc352298goroutinecount=4gofunc352299goroutinecount=4gofunc352300goroutinecount=4gofunc352301goroutinecount=4gofunc352302goroutinecount=4...

从结果看,程序并没有出现崩溃,而是按部就班的顺序执行,并且go的数量控制在了3,(4的原因是因为还有一个maingoroutine)那么从数字上看,是不是在跑的goroutines有几十万个呢?

这里我们用了buffer为3的channel,在代码往channel写的过程中,实际上是限制了速度的:

fori:=0;i<go_cnt;i++{ch<-truegobusi(ch,i)}

for循环里ch<-true的速度,因为这个速度决定了go的创建速度,而go的结束速度取决于doBusiness()函数的执行速度。这样实际上,我们就能够保证了,同一时间内运行的goroutine的数量与buffer的数量一致,从而达到了限定效果。

但是这段代码有一个小问题,就是如果我们把go_cnt的数量变的小一些,会出现打出的结果不正确。

packagemainimport("fmt"//"math""runtime")funcdoBusiness(chchanbool,iint){fmt.Println("gofunc:",i,"goroutinecount:",runtime.NumGoroutine())<-ch}funcmain(){//max_cnt:=math.MaxInt64max_cnt:=10ch:=make(chanbool,3)fori:=0;i<max_cnt;i++{ch<-truegodoBusiness(ch,i)}}

结果:

gofunc2goroutinecount=4gofunc3goroutinecount=4gofunc4goroutinecount=4gofunc5goroutinecount=4gofunc6goroutinecount=4gofunc1goroutinecount=4gofunc8goroutinecount=4

可以看到有些goroutine没有打印出来,是由于main把所有goroutine开启之后,main就直接退出了,我们知道main进程退出,低下所有的goroutine都会结束掉,从而导致有些goroutine还没来得及执行就退出了。所以想全部go都执行,需要在main的最后进行阻塞操作。

方法二:使用sync同步机制import("fmt""math""sync""runtime")varwg=sync.WaitGroup{}funcdoBusiness(iint){fmt.Println("gofunc",i,"goroutinecount=",runtime.NumGoroutine())wg.Done()}funcmain(){//模拟用户需求业务的数量task_cnt:=math.MaxInt64fori:=0;i<task_cnt;i++{wg.Add(1)godoBusiness(i)}wg.Wait()}

很明显,如果单纯的使用sync也达不到控制goroutine的数量,最终结果依然是崩溃。

方法三:channel与sync同步组合方式实现控制goroutinepackagemainimport("fmt""math""sync""runtime")varwg=sync.WaitGroup{}funcdoBusiness(chchanbool,iint){fmt.Println("gofunc",i,"goroutinecount=",runtime.NumGoroutine())<-chwg.Done()}funcmain(){//模拟用户需求go业务的数量task_cnt:=math.MaxInt64ch:=make(chanbool,3)fori:=0;i<task_cnt;i++{wg.Add(1)ch<-truegodoBusiness(ch,i)}wg.Wait()}

结果:

//...gofunc228856goroutinecount=4gofunc228857goroutinecount=4gofunc228858goroutinecount=4gofunc228859goroutinecount=4gofunc228860goroutinecount=4gofunc228861goroutinecount=4gofunc228862goroutinecount=4gofunc228863goroutinecount=4gofunc228864goroutinecount=4gofunc228865goroutinecount=4gofunc228866goroutinecount=4gofunc228867goroutinecount=4//...

这样我们程序就不会再造成资源爆炸而崩溃。而且运行go的数量控制住了在buffer为3的这个范围内。

方法四:利用无缓冲channel与任务发送/执行分离方式signal:killed0

执行流程大致如下,这里实际上是将任务的发送和执行做了业务上的分离。使得消息出去,输入SendTask的频率可设置、执行Goroutine的数量也可设置。也就是既控制输入(生产),又控制输出(消费)。使得可控更加灵活。这也是很多Go框架的Worker工作池的最初设计思想理念。

如下图:

以上是今天跟大家分享的内容,更多优质的技术文章欢迎关注公号【Go键盘侠】。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
生产要素的需求有哪些性质 生产要素的需求有何特点? 什么是生产要素需求 微观经济学要素需求什么是条件要素需求?它和要素需求有什么不同?_百度... 养宠物的人遵守规则,是不是就能和别人平安相处呢? 企业培训学到了什么 培训感悟简短 有关培训的感悟 通过培训学到什么 培训你学到了什么 领导问培训学到什么怎么回复 浅谈广州早茶文化:带你认识真正的广州早茶 佳能IXUS990IS重要参数 let's GoLang(三): Goroutine&amp;Channel 茶文化 | 广州的早茶文化 广州茶道早茶茶道 佳能ixus185有哪些优缺点? 女儿订婚仪式父母亲发言说什么 女方妈妈订婚致辞简短精辟 javascript里./是什么意思 javascript里的;怎么回事? 微信消息突然不提示了怎么回事 天国的女儿表达的意义 ...如丝如缕.(2分)江上飘着一片片白帆,像一幅画,又像一首诗._百度... ...卡,办理信用卡时未经过我同意填写了我的手机号码,现在他已经离职走了... Master是什么学位, Postgraduate又是什么? master专业是什么意思 TP-LINK TL-SF1024D其它参数 TP-LINK TL-SF1024D重要参数 QQ一笔画红包第1关怎么画_一笔画红包关卡1关画法攻略 地下城第三季鬼泣纯刷图加点,复制的滚 广州的广式早茶文化是怎么样的? golang 日志切割库 goroutine 泄漏导致进程 panic 问题排查 广州茶道-饮凉茶、宵夜、饮糖水 每天一个知识点:Goroutine 调度器过程及原理 电脑蓝屏蓝屏之后就会自动重启,然后还是蓝屏 笔记本电脑突然蓝屏,然后自动修复重启,但重启后屏幕就一直黑着,强 很晚睡却很早就自然醒正常吗-很晚睡却很早就自然醒头晕怎么办_百度知 ... ...人,睡前做好哪些小动作,或许让你一觉睡到自然醒? ...睡得不香,很少有一觉睡到自然醒的时候,有什么办法改善? 咸鱼之王珍珠怎么获取-珍珠获取方法 N73手机重新关开机是什么问题 N73怎么在一般情况下,运作内存只剩下十几M 有请高手 1010135299@QQ。com... 寻N73软件…… N73主题模式进不去了,怎么办? 用诺基亚N系列手机的进来帮忙救命的 梦到黑蚂蚁成堆是什么兆头 从天津站坐462到哪站下车能到天津市汉沽区新开中路96号科委院内,急 天津市科委主要职责 想知道: 天津市 从天津市到宝坻区科委怎么坐公交 想知道: 天津市 从天津市到汉沽开发区怎么坐公交