为什么要控制goroutine的数量?

在我们开发过程中,如果不对goroutine加以控制而进行滥用的话,可能会导致服务整体崩溃。比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。

用什么方法控制goroutine的数量?

尝试 chan

func main() {
userCount := 10
ch := make(chan bool, 2)
for i := 0; i < userCount; i++ {
ch <- true
go Read(ch, i)
} //time.Sleep(time.Second)
} func Read(ch chan bool, i int) {
fmt.Printf("go func: %d\n", i)
<- ch
}

输出结果:

go func: 1
go func: 2
go func: 3
go func: 4
go func: 5
go func: 6
go func: 7
go func: 8
go func: 0

但是新的问题出现了,因为并不是所有的goroutine都执行完了,在main函数退出之后,还有一些goroutine没有执行完就被强制结束了。这个时候我们就需要用到sync.WaitGroup。使用WaitGroup等待所有的goroutine退出。如下:

尝试 sync.WaitGroup

var wg *sync.WaitGroup

func work() {
defer wg.Done()
//do something
} func main() {
wg = &sync.WaitGroup{}
for i:=0; i < 10000; i++ {
wg.Add(1)
go work()
}
wg.Wait()//等待所有goroutine退出
}

单纯的使用 sync.WaitGroup 也不行。没有控制到同时并发的 goroutine 数量

尝试chan + sync

package main 

import (
"fmt"
"runtime"
"sync"
) var wg = sync.WaitGroup{} // 任务业务流程
func business(ch chan bool, i int) {
fmt.Println("go func", i, " goroutine count = ", runtime.NumGoroutine) <-ch wg.Done()
} func main() {
// 模拟用户需求的业务数量
task_cnt := 10 ch := make(chan bool, 3) for i := 0; i < taskk_cnt; i++ {
wg.Add(1) // 如果channel满了,就会阻塞
ch <- true // 开启一个新协程
go business(ch, i)
} wg.Wait()
}

⽆缓冲channel和任务发送/执⾏分离来限制(协程池)

package main 

import (
"fmt"
"runtime"
"sync"
) var wg = sync.WaitGroup{} // 每个go的worker都要执行的一个工作流程
func business(ch chan int){
// 消费一个任务
for t := range ch {
fmt.Println(" go task = ", t, ", goroutine count = ", runtime.NumGoroutine()) wg.Done()
}
} // 发送一个任务(任务的输入,任务的生产)
func sendTask(task int, ch chan int) {
wg.Add(1) ch <- task
} func main() {
// 无buffer的channel
ch := make(chan int) // 1 启动goroutine工作池(go的数量是固定的)充当任务task的消费
goCnt := 3
for i := 0; i < goCnt; i++ {
// 启动goroutine的worker
go business(ch)
} // 2模拟用户需求业务的数量,不断的给工作池发送task
taskCnt := math.MaxInt64 for t := 0; t < taskCnt; t++ {
// 发送任务
sendTask(t, ch)
} wg.Wait()
}

控制goroutine的数量,封装一下

package gpool

import (
"sync"
) type pool struct {
queue chan int
wg *sync.WaitGroup
} func New(size int) *pool {
if size <= 0 {
size = 1
}
return &pool{
queue: make(chan int, size),
wg: &sync.WaitGroup{},
}
} func (p *pool) Add(delta int) {
for i := 0; i < delta; i++ {
p.queue <- 1
}
for i := 0; i > delta; i-- {
<-p.queue
}
p.wg.Add(delta)
} func (p *pool) Done() {
<-p.queue
p.wg.Done()
} func (p *pool) Wait() {
p.wg.Wait()
}

测试代码:

package gpool_test

import (
"runtime"
"testing"
"time"
"gpool"
) func Test_Example(t *testing.T) {
pool := gpool.New(100)
println(runtime.NumGoroutine())
for i := 0; i < 1000; i++ {
pool.Add(1)
go func() {
time.Sleep(time.Second)
println(runtime.NumGoroutine())
pool.Done()
}()
}
pool.Wait()
println(runtime.NumGoroutine())
}

如何限制goroutine的数量的更多相关文章

  1. 如何优雅的控制goroutine的数量

    1,为什么要控制goroutine的数量? goroutine固然好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来.比如: ; i < ...

  2. 限制goroutine数量写法

    虽然golang的goroutine可以开启无数个goroutine,但是没有限制也是不行的.我就写一下我对goroutine数量限制的写法 1.初始化goroutine协程池.把goroutine数 ...

  3. go中控制goroutine数量

    控制goroutine数量 前言 控制goroutine的数量 通过channel+sync 使用semaphore 线程池 几个开源的线程池的设计 fasthttp中的协程池实现 Start Sto ...

  4. golang使用pprof检查goroutine泄露

    有一段时间,我们的推送服务socket占用非常不正常,我们自己统计的同一时候在线就10w的用户,可是占用的socket居然达到30w,然后查看goroutine的数量,发现已经60w+. 每一个用户占 ...

  5. Golang 探索对Goroutine的控制方法

    前言 在golang中,只需要在函数调用前加上关键字go即可创建一个并发任务单元,而这个新建的任务会被放入队列中,等待调度器安排.相比系统的MB级别线程栈,goroutine的自定义栈只有2KB,这使 ...

  6. 四种方式实现子goroutine与主线程的同步

    如何实现子goroutine与主线程的同步 第一种方式: 这种方式很太死板,就不演示了. 第二种方式:使用 channel机制,每个 goroutine传一个 channel进去然后往里写数据,在再主 ...

  7. [Go] golang缓冲通道实现管理一组goroutine工作

    通道1.当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道2.无缓冲通道和有缓冲通道,make的第二个参数就是缓冲区大小3.无缓冲通道需要发送和接收都准备好,否则 ...

  8. 使goroutine同步的方法总结

    前言: 在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程. 但当多个goroutine同时进行处理的时候,就会遇 ...

  9. Goroutine并发调度模型深度解析之手撸一个协程池

    golanggoroutine协程池Groutine Pool高并发 并发(并行),一直以来都是一个编程语言里的核心主题之一,也是被开发者关注最多的话题:Go语言作为一个出道以来就自带 『高并发』光环 ...

  10. 第三章 Goroutine调度策略(16)

    本文是<Go语言调度器源代码情景分析>系列的第16篇,也是第三章<Goroutine调度策略>的第1小节. 在调度器概述一节我们提到过,所谓的goroutine调度,是指程序代 ...

随机推荐

  1. Qt/C++音视频开发75-获取本地有哪些摄像头名称/Qt内置函数方式

    一.前言 在需要打开本地摄像头的场景中,有个需求绕不开,那就是如何获取本地有哪些摄像头设备名称,这样可以提供下拉框给用户选择,不然你让用户去填设备名,你觉得用户会知道是啥,他会操作吗?就算你提供了详细 ...

  2. Qt编写跨平台RTSP/RTMP/HTTP视频流播放器

    一.前言 很早以前就做过这款播放器的入门版本,最开始用的ffmpeg去解析,后面陆续用vlc播放器.mpv播放器来做,毕竟播放器提供的接口使用也很方便,而且功能强大,后面发现播放器主要的应用场景是播放 ...

  3. 《jQueryEasyUI从零开始学》-施尧2018一书的配套源代码和学习资源

    <jQueryEasyUI从零开始学>-施尧2018一书的配套源代码和学习资源:下载地址 提取码:uuly

  4. 阿里IM技术分享(十):深度揭密钉钉后端架构的单元化演进之路

    本文由钉钉技术专家啸台.万泓分享,为了获得更好的阅读效果,本文已对内容进行少修订和重新排版. 1.引言 钉钉后端架构的单元化工作从2018年开始到今年,已经是第五个年头了.五年的时间,钉钉单元化迭代了 ...

  5. 老生常谈——分布式限流:部分Sentinal源码解读

    基础知识 HTTP CODE = 429 "请求过多" A. 限流的类型 服务端 客户端 限流的标的 IP 用户 ... 基本要求 准确限制过量的请求. 低延时.限流器不能拖慢HT ...

  6. Solution -「ZJOI 2015」「洛谷 P3343」地震后的幻想乡

    \(\mathscr{Description}\)   Link.   给定连通图简单无向 \(G=(V,E)\),对于 \(e\in E\),有边权 \(t_e\) 独立均匀随机生成自 \([0,1 ...

  7. C# Moq - Non-overridable members may not be used in setup / verification expressions

    测试: public class UnitTest1 { [TestMethod] public void TestMethod1() { Mock<TestClass> moc = ne ...

  8. mac sublime text3-快捷键

    cmd+n 新建页面 cmd+数字键 切换到对应页面 cmd+p 搜索跳转到对应页 cmd+w 关闭页面 cmd+j 合并一行 cmd+d 选中当前单词,继续敲可以选中多个 cmd+l 选中当前行 c ...

  9. 基于Pamion的流实数仓架构

    目录 1. 背景 2. 目标 3. Pamion 的概念和设计 3.1 架构 3.2 统一存储 3.3 基础概念 3.3.1 文件布局 3.3.2 Snapshot 3.3.3 Manifest 文件 ...

  10. SSL 和 TLS

    转载:链接1   链接2 TLS和SSL SSL(Secure Sockets Layer)安全套接层协议 TLS(Transport Layer Security)传输层安全性协议 最新版本的TLS ...