来,控制一下 goroutine 的并发数量

对 golang 中的 goroutine 数量进行控制

简介

  • golang 中开启一个 goroutine 是一件很便捷的事情
  • 但是如果不对其进行控制,则可能造成 goroutine 泛滥成灾
  • 本文就对如何控制 goroutine 数量进行一个尝试
  • 主要思路是通过 chan 实现,取一个协程就向 chan 中写入,如果协程被取完则会阻塞,用完一个协程就从通道中拿出一个即可

代码

  • for 循环中不停的开启新的协程
  • 每隔一秒钟打印当前的协程数

没有协程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"runtime"
"time"
)

func main() {
withoutGoroutinePool()
}

func withoutGoroutinePool() {
ticker := time.NewTicker(time.Second)
for {
select {
case <-ticker.C:
fmt.Println(time.Now().String(), runtime.NumGoroutine())
break
default:
go sleep()
}
}

}

func sleep() {
time.Sleep(100 * time.Millisecond)
}
  • 输出结果如下所示,可以看出 Goroutine 的数量超多,在几十万之多,可能会瞬间打满 cpu
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ➜  aaa git:(dev) ✗ go run main.go
    2020-02-10 22:21:07.708449 +0800 CST m=+1.075829140 258734
    2020-02-10 22:21:08.63268 +0800 CST m=+2.000078344 340130
    2020-02-10 22:21:09.63266 +0800 CST m=+3.000079432 394476
    2020-02-10 22:21:10.632632 +0800 CST m=+4.000071378 418946
    2020-02-10 22:21:11.632619 +0800 CST m=+5.000079498 406332
    2020-02-10 22:21:12.632586 +0800 CST m=+6.000066992 406157
    2020-02-10 22:21:13.632612 +0800 CST m=+7.000113855 366507
    2020-02-10 22:21:14.632658 +0800 CST m=+8.000180030 369627
    2020-02-10 22:21:15.632572 +0800 CST m=+9.000114252 397764
    ^Csignal: interrupt

有协程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
"fmt"
"runtime"
"time"
)

type goroutinePool struct {
c chan interface{}
}

var pool *goroutinePool

func newGoroutinePool(size int) *goroutinePool {
return &goroutinePool{c: make(chan interface{}, size)}
}

func (goroutinePool *goroutinePool) Get() {
goroutinePool.c <- struct{}{}
}

func (goroutinePool *goroutinePool) Release() {
<-goroutinePool.c
}

func withGoroutinePool() {
pool = newGoroutinePool(100)
ticker := time.NewTicker(time.Second)
for {
select {
case <-ticker.C:
fmt.Println(time.Now().String(), runtime.NumGoroutine())
break
default:
pool.Get()
go func() {
sleep()
pool.Release()
}()
}
}
}

func main() {
withGoroutinePool()
}

func sleep() {
time.Sleep(100 * time.Millisecond)
}
  • 输出结果如下所示,可以看出 Goroutine 的数量在 100 左右
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ➜  aaa git:(dev) ✗ go run main.go
    2020-02-10 22:23:57.4038 +0800 CST m=+1.017452302 45
    2020-02-10 22:23:58.420406 +0800 CST m=+2.034076062 38
    2020-02-10 22:23:59.450896 +0800 CST m=+3.064585392 86
    2020-02-10 22:24:00.463936 +0800 CST m=+4.077642981 80
    2020-02-10 22:24:01.480972 +0800 CST m=+5.094697456 71
    2020-02-10 22:24:02.398438 +0800 CST m=+6.012179639 101
    2020-02-10 22:24:03.416983 +0800 CST m=+7.030743402 88
    2020-02-10 22:24:04.444314 +0800 CST m=+8.058093131 3
    2020-02-10 22:24:05.457963 +0800 CST m=+9.071760019 78
    ^Csignal: interrupt