如何优雅的控制goroutine的数量

1,为什么要控制goroutine的数量?

goroutine固然好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。比如:

1 for i:=0; i < 10000; i++ {
2     go work()
3 }

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

要在每一次执行go之前判断goroutine的数量,如果数量超了,就要阻塞go的执行。第一时间想到的就是使用通道。每次执行的go之前向通道写入值,直到通道满的时候就阻塞了,如下:

 1 var ch chan int
 2
 3 func work() {
 4     //do something
 5     <-ch
 6 }
 7
 8 func main() {
 9     ch = make(chan int, 10)
10     for i:=0; i < 10000; i++ {
11        ch <- 1
12        go work()
13     }
14 }

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

 1 var wg *sync.WaitGroup
 2
 3 func work() {
 4     defer wg.Done()
 5     //do something
 6 }
 7
 8 func main() {
 9     wg = &sync.WaitGroup{}
10     for i:=0; i < 10000; i++ {
11        wg.Add(1)
12        go work()
13     }
14     wg.Wait()//等待所有goroutine退出
15 }

3,优雅的使用并控制goroutine的数量

综上所述,我们封装一下,代码如下:

 1 package gpool
 2
 3 import (
 4     "sync"
 5 )
 6
 7 type pool struct {
 8     queue chan int
 9     wg    *sync.WaitGroup
10 }
11
12 func New(size int) *pool {
13     if size <= 0 {
14         size = 1
15     }
16     return &pool{
17         queue: make(chan int, size),
18         wg:    &sync.WaitGroup{},
19     }
20 }
21
22 func (p *pool) Add(delta int) {
23     for i := 0; i < delta; i++ {
24         p.queue <- 1
25     }
26     for i := 0; i > delta; i-- {
27         <-p.queue
28     }
29     p.wg.Add(delta)
30 }
31
32 func (p *pool) Done() {
33     <-p.queue
34     p.wg.Done()
35 }
36
37 func (p *pool) Wait() {
38     p.wg.Wait()
39 }

来段测试代码:

 1 package gpool_test
 2
 3 import (
 4     "runtime"
 5     "testing"
 6     "time"
 7     "gpool"
 8 )
 9
10 func Test_Example(t *testing.T) {
11     pool := gpool.New(100)
12     println(runtime.NumGoroutine())
13     for i := 0; i < 1000; i++ {
14         pool.Add(1)
15         go func() {
16             time.Sleep(time.Second)
17             println(runtime.NumGoroutine())
18             pool.Done()
19         }()
20     }
21     pool.Wait()
22     println(runtime.NumGoroutine())
23 }

good job,Over~

时间: 2024-12-11 02:14:44

如何优雅的控制goroutine的数量的相关文章

深度分析如何在Hadoop中控制Map的数量

深度分析如何在Hadoop中控制Map的数量 [email protected] 很多文档中描述,Mapper的数量在默认情况下不可直接控制干预,因为Mapper的数量由输入的大小和个数决定.在默认情况下,最终input 占据了多少block,就应该启动多少个Mapper.如果输入的文件数量巨大,但是每个文件的size都小于HDFS的blockSize,那么会造成 启动的Mapper等于文件的数量(即每个文件都占据了一个block),那么很可能造成启动的Mapper数量超出限制而导致崩溃.这些逻

Hadoop MR Job 关于如何控制Map Task 数量

整理下,基本分两个方式: 一.对于大量大文件(大于block块设置的大小) 增大minSize,即增大mapred.min.split.size的值,原因:splitsize=max(minisize,min(maxsize,blocksize)),blocksize一般不会做修改. 在没有设置minisize,maxsize时,splitsize取blocksize. 二.对于大量小文件(小于block块设置的大小) 这种情况通过增大mapred.min.split.size不可行, 需要使用

Go语言基础之并发

并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天). 并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天). Go语言的并发通过goroutine实现.goroutine类似于线程,属于用户态的线程,我们可以根据需要创建成千上万个goroutine并发工作.goroutine是由Go语言的运行时(runtime)调度完成,而线程是由操作系

如何优雅地等待所有的goroutine退出

Table of Contents 1. 通过Channel传递退出信号 2. 使用waitgroup goroutine和channel是Go语言非常棒的特色,它们提供了一种非常轻便易用的并发能力.但是当您的应用进程中有很多goroutine的时候,如何在主流程中等待所有的goroutine 退出呢? 1 通过Channel传递退出信号 Go的一大设计哲学就是:通过Channel共享数据,而不是通过共享内存共享数据.主流程可以通过channel向任何goroutine发送停止信号,就像下面这样

Hadoop2.6.0的FileInputFormat的任务切分原理分析(即如何控制FileInputFormat的map任务数量)

前言 首先确保已经搭建好Hadoop集群环境,可以参考<Linux下Hadoop集群环境的搭建>一文的内容.我在测试mapreduce任务时,发现相比于使用Job.setNumReduceTasks(int)控制reduce任务数量而言,控制map任务数量一直是一个困扰我的问题.好在经过很多摸索与实验,终于梳理出来,希望对在工作中进行Hadoop进行性能调优的新人们有个借鉴.本文只针对FileInputFormat的任务划分进行分析,其它类型的InputFormat的划分方式又各有不同.虽然如

[转]新兵训练营系列课程——编写优雅代码

原文:http://weibo.com/p/1001643877361430185536 课程大纲 什么是好代码 如何编写优雅的代码 如何做出优雅的设计 如何规划合理的架构 如何处理遗留代码 什么是好代码 对于代码质量的定义需要于从两个维度分析:主观的,被人类理解的部分:还有客观的,在计算机里运行的状况. 我把代码质量分为五个层次,依次为: 完成功能的代码 高性能的代码 易读的代码 可测试的代码 可扩展的代码 如何编写可读的代码 在很多跟代码质量有关的书里都强调了一个观点:程序首先是给人看的,其

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

golanggoroutine协程池Groutine Pool高并发 并发(并行),一直以来都是一个编程语言里的核心主题之一,也是被开发者关注最多的话题:Go语言作为一个出道以来就自带 『高并发』光环的富二代编程语言,它的并发(并行)编程肯定是值得开发者去探究的,而Go语言中的并发(并行)编程是经由goroutine实现的,goroutine是golang最重要的特性之一,具有使用成本低.消耗资源低.能效高等特点,官方宣称原生goroutine并发成千上万不成问题,于是它也成为Gopher们经常

使goroutine同步的方法总结

前言: 在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程. 但当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续.sync包就是为了让goroutine同步而出现的.当然还可以使用channel实现,这个后面会介绍到. 锁: 锁有两种:互斥锁(mutex)和读写锁(RWMu

Goroutine并发控制

目录 创建协程 控制Gorutine的数量 创建协程 jobCount := 10 // sync.WaitGroup 监控所有协程的状态,从而保证主协程结束时所有的子协程已经退出 group := sync.WaitGroup{} for i:=0;i < jobCount;i++ { group.Add(1) go func(i int) { fmt.Println("task ",i) time.Sleep(time.Second) // 刻意睡 1 秒钟,模拟耗时 gro