23
2020
11

怎么限制Goroutine的数量.

在Golang中,Goroutine虽然很好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。所以我们可以限制下Goroutine的数量,这样就需要在每一次执行go之前判断goroutine的数量,如果数量超了,就要阻塞go的执行。第一时间想到的就是使用通道。每次执行的go之前向通道写入值,直到通道满的时候就阻塞了,


package main


import "fmt"


var ch chan  int


func elegance(){

 <-ch

 fmt.Println("the ch value receive",ch)

}


func main(){

 ch = make(chan int,5)

 for i:=0;i<10;i++{

  ch <-1

  fmt.Println("the ch value send",ch)

  go elegance()

  fmt.Println("the result i",i)

 }


}

运行:


> go run goroutine.go 

the ch value send 0xc00009c000

the result i 0

the ch value send 0xc00009c000

the result i 1

the ch value send 0xc00009c000

the result i 2

the ch value send 0xc00009c000

the result i 3

the ch value send 0xc00009c000

the result i 4

the ch value send 0xc00009c000

the result i 5

the ch value send 0xc00009c000

the ch value receive 0xc00009c000

the result i 6

the ch value receive 0xc00009c000

the ch value send 0xc00009c000

the result i 7

the ch value send 0xc00009c000

the result i 8

the ch value send 0xc00009c000

the result i 9

the ch value send 0xc00009c000

the ch value receive 0xc00009c000

the ch value receive 0xc00009c000

the ch value receive 0xc00009c000

the result i 10

the ch value send 0xc00009c000

the result i 11

the ch value send 0xc00009c000

the result i 12

the ch value send 0xc00009c000

the result i 13

the ch value send 0xc00009c000

the ch value receive 0xc00009c000

the ch value receive 0xc00009c000

the ch value receive 0xc00009c000

the ch value receive 0xc00009c000

the result i 14

the ch value receive 0xc00009c000

> go run goroutine.go 

the ch value send 0xc00007e000

the result i 0

the ch value send 0xc00007e000

the result i 1

the ch value send 0xc00007e000

the result i 2

the ch value send 0xc00007e000

the result i 3

the ch value send 0xc00007e000

the ch value receive 0xc00007e000

the result i 4

the ch value send 0xc00007e000

the ch value receive 0xc00007e000

the result i 5

the ch value send 0xc00007e000

the ch value receive 0xc00007e000

the result i 6

the ch value send 0xc00007e000

the result i 7

the ch value send 0xc00007e000

the ch value receive 0xc00007e000

the ch value receive 0xc00007e000

the ch value receive 0xc00007e000

the result i 8

the ch value send 0xc00007e000

the result i 9

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


package main


import (

 "fmt"

 "runtime"

 "sync"

 "time"

)

// Pool Goroutine Pool

type Pool struct {

 queue chan int

 wg *sync.WaitGroup

}

// New 新建一个协程池

func NewPool(size int) *Pool{

 if size <=0{

  size = 1

 }

 return &Pool{

  queue:make(chan int,size),

  wg:&sync.WaitGroup{},

 }

}

// Add 新增一个执行

func (p *Pool)Add(delta int){

 // delta为正数就添加

 for i :=0;i<delta;i++{

  p.queue <-1

 }

 // delta为负数就减少

 for i:=0;i>delta;i--{

  <-p.queue

 }

 p.wg.Add(delta)

}

// Done 执行完成减一

func (p *Pool) Done(){

 <-p.queue

 p.wg.Done()

}

// Wait 等待Goroutine执行完毕

func (p *Pool) Wait(){

 p.wg.Wait()

}


func main(){

 // 这里限制5个并发

 pool := NewPool(5)

 fmt.Println("the NumGoroutine begin is:",runtime.NumGoroutine())

 for i:=0;i<20;i++{

  pool.Add(1)

  go func(i int) {

   time.Sleep(time.Second)

   fmt.Println("the NumGoroutine continue is:",runtime.NumGoroutine())

   pool.Done()

  }(i)

 }

 pool.Wait()

 fmt.Println("the NumGoroutine done is:",runtime.NumGoroutine())

}

运行:


the NumGoroutine begin is: 1

the NumGoroutine continue is: 6

the NumGoroutine continue is: 7

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 6

the NumGoroutine continue is: 3

the NumGoroutine continue is: 2

the NumGoroutine done is: 1

其中,Go的GOMAXPROCS默认值已经设置为CPU的核数, 这里允许我们的Go程序充分使用机器的每一个CPU,最大程度的提高我们程序的并发性能。runtime.NumGoroutine函数在被调用后,会返回系统中的处于特定状态的Goroutine的数量。这里的特指是指Grunnable\Gruning\Gsyscall\Gwaition。处于这些状态的Groutine即被看做是活跃的或者说正在被调度。


这里需要注意下:垃圾回收所在Groutine的状态也处于这个范围内的话,也会被纳入该计数器。

« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。