写在文章开头
我们都说协程是活跃于应用层的轻量级线程,进行调度切换时的开销远远小于线程,那么问题来了,对于高并发的场景,协程是否是越多越好呢?对此本文就会基于几个案例和go语言设计层面的角度来剖析这个问题。
Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili 。
因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。
多协程代码示例
我们给出一段代码示例,这段代码会进行一个大循环创建多个协程调用foo方法:
func foo() { //休眠1s后输出 time.Sleep(time.Second) }
func main() {
//大循环启动大量协程
for i := 0; i < math.MaxInt64; i++ {
go func() {
foo()
}()
}
//休眠 让上述协程执行
time.Sleep(time.Hour)
}
程序执行完一段时间后报错,提示操作了大量的文件或者socket
,很明显go在使用协程提升并发性能的同时也对服务器资源的使用做了限制。
所以,对于协程的使用,我们应做到如何使用尽可能少的协程做尽快尽多的任务:
panic: too many concurrent operations on a single file or socket (max 1048575)
goroutine 1162454 [running]:
协程并发最佳实践
逻辑优化
协程设计的初衷就是线程用后即焚,所以从业角度考虑,假设我们开大量协程执行耗时任务,这就会导致单位时间内有大量的协程都活跃与内存中,无法做到即用即毁,随着时间的推移就看你会把内存资源耗尽。所有我们建议在使用协程进行业务开发时,尽可能的优化任务的执行速度。
以我们上文为例,造成too many concurrent operations on a single file or socket
的本质原因就是内存中存在大量的协撑爆了服务器的内存资源,所以如果我们能够提升每一个协程的执行速度,保证单位时间内尽可能处理多的协程,保证大量协程能够及时GC以保证时刻保持足量的内存空间,就能够避免上述问题:
对应我们将foo休眠的逻辑去掉,以模拟协程毫秒级处理,最终代码正确执行完成了:
func foo() { //模拟协程毫秒级的处理 }
func main() {
i := 0
//大循环启动大量协程
for ; i < math.MaxInt64; i++ {
go func() {
foo()
}()
}
//等待上述协程执行完成
var wg sync.WaitGroup
wg.Add(1)
go func() {
for true {
//当所有协程执行完成之后,该协程退出循环
if i == math.MaxInt64 {
wg.Done()
break
}time.Sleep(1 * time.Minute)
}}()
//所有协程执行完成后输出finish
wg.Wait()
fmt.Println(“finish”)
}
利用channel实现协程有界化
产生上述的原因就是单位时间内创建了太多了协程,所以我们可以通过有缓冲区的channel
作为信号量,只有信号可以投递到缓冲区中才能创建协程,由此实现单位时间内仅能创建有限量的协程,从而避免内存溢出:
对应的我们给出这段思路的代码:
func foo(ch chan struct{}) { fmt.Println("hello") time.Sleep(time.Second) //完成执行后,将信号从channel中抛出 <-ch }
func main() {
//创建channel允许容纳5000个元素
ch := make(chan struct{}, 5000)//创建大量协程
for i := 0; i < math.MaxInt64; i++ {
//投递信号到协程中,只有投递成功才能执行后续逻辑
ch <- struct{}{}
//创建协程并运行
go func() {
foo(ch)
}()
}
}
协程池化
从一个Java程序员的角度出发,大部分都认为协程应该可以向线程一样通过池化进行复用,但是我们不推荐这种做法,因为go语言本身就内置对协程池化的管理机制,并且协程的使用初衷就是即用即毁,避免池化,所以这个方案我们不推荐。
终极大招
提升服务器硬件配置,俗称:换个高配CPU、加内存。
小结
以上便是笔者对于协程创建的量的问题的思考和总结,希望对你有帮助。
我是 sharkchili ,CSDN Java 领域博客专家,开源项目—JavaGuide contributor,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号: 写代码的SharkChili 。 因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。
这是一个从 https://juejin.cn/post/7369052013152043060 下的原始话题分离的讨论话题