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退出。如下:
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()
}
来段测试代码:
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~
来源:oschina
链接:https://my.oschina.net/u/70773/blog/682884