如何優雅的控制goroutine的數量

1,爲何要控制goroutine的數量?     goroutine當然好,可是數量太多了,每每會帶來不少麻煩,好比耗盡系統資源致使程序崩潰,或者CPU使用率太高致使系統忙不過來。好比:函數

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

2,用什麼方法控制goroutine的數量? 要在每一次執行go以前判斷goroutine的數量,若是數量超了,就要阻塞go的執行。第一時間想到的就是使用通道。每次執行的go以前向通道寫入值,直到通道滿的時候就阻塞了,以下:測試

var ch chan int

func work() {
    //do something
    <-ch
}

func main() {
    ch = make(chan int, 10)
    for i:=0; i < 10000; i++ {
       ch <- 1
       go work()
    }
}

這樣每次同時運行的goroutine就被限制爲10個了。可是新的問題出現了,由於並非全部的goroutine都執行完了,在main函數退出以後,還有一些goroutine沒有執行完就被強制結束了。這個時候咱們就須要用到sync.WaitGroup。使用WaitGroup等待全部的goroutine退出。以下:code

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退出
}

3,優雅的使用並控制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()
}

來段測試代碼:it

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())
}

good job,Over~test

相關文章
相關標籤/搜索