「Goroutineの最大数を制御する方法」を試してみた

並列に HTTP リクエストを実行するんだけど、並列数は一定までに保ちたい...
と思って、Go なら簡単にできるんじゃないかと思ってみたところ、想像以上に簡単だということがわかりました。

以下の記事を読みました。
[Goroutineの最大数を制御する方法](http://qiita.com/ruiu/items/d4ef16981b870a1b85af)

要するに

* チャネル生成時にバッファ数を指定する
* Goroutine 起動時にチャネルに値を送る
* Goroutine 終了時にチャネルから値を出す

ということをやるだけでできてしまいます。

これを試すコードを書いてみました。

```go
package main

import (
	"fmt"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"runtime"
	"sync"
	"time"
)

// Goroutine の数を数えておく
var gCnt = 0

// Goroutine の ID 発番
var gId = 1

// ループを実行する回数の設定
var loopCount = 100

func main() {
	// プロファイラを起動してブラウザで確認できるようにしておく
	// http://localhost:6060/debug/pprof/goroutine?debug=2 とかで Goroutine の様子を確認できる
	runtime.SetBlockProfileRate(1)
	go func() {
		http.ListenAndServe(":6060", nil)
	}()

	mutex := &sync.Mutex{}
	wg := &sync.WaitGroup{}
	wg.Add(loopCount)

	c := make(chan bool, 3)
	for i := 1; i <= loopCount; i++ {
		c <- true
		go func() {
			mutex.Lock()
			gCnt++
			updateGoroutineCount()
			myGid := gId
			fmt.Printf("%d is began\n", gId)
			gId++
			mutex.Unlock()

			time.Sleep(time.Duration(rand.Int31n(100)) * time.Millisecond)
			defer func() {
				mutex.Lock()
				fmt.Printf("%d is finished\n", myGid)
				gCnt--
				updateGoroutineCount()
				mutex.Unlock()

				wg.Done()
				<-c
			}()
		}()
	}

	wg.Wait()
}

func updateGoroutineCount() {
	fmt.Printf("%d goroutines exist\n", gCnt)
}
```

元記事のサンプルコードを元に、以下のような変更を加えています。

* Goroutine に ID を割り当て、起動・終了時にはその旨を出力する
* 各 Goroutine はランダムな時間だけ Sleep して終了
* その時点での Goroutine の並列数を変数に保持しておいて、Goroutine が起動・終了するたびにその数を出力
* `net/http/pprof` を起動しておいて、ブラウザから Goroutine の様子を確認できるようにする

これの実行結果は以下の通り。

```
EMTG-MAC08:keep-goroutine-count (master*) $ go run main.go
1 goroutines exist
1 is began
2 goroutines exist
2 is began
3 goroutines exist
3 is began
3 is finished
2 goroutines exist
3 goroutines exist
4 is began
1 is finished
2 goroutines exist
2 is finished
1 goroutines exist
2 goroutines exist
5 is began
3 goroutines exist
6 is began
6 is finished
2 goroutines exist
4 is finished
1 goroutines exist
2 goroutines exist
7 is began
3 goroutines exist
8 is began
7 is finished
2 goroutines exist
3 goroutines exist
9 is began
8 is finished
2 goroutines exist
3 goroutines exist
10 is began
10 is finished
2 goroutines exist
3 goroutines exist
11 is began
5 is finished
2 goroutines exist
3 goroutines exist
12 is began
12 is finished
2 goroutines exist
3 goroutines exist
13 is began
9 is finished
2 goroutines exist
3 goroutines exist
14 is began
11 is finished
2 goroutines exist
13 is finished
1 goroutines exist
2 goroutines exist
15 is began
3 goroutines exist
16 is began
15 is finished
2 goroutines exist
3 goroutines exist
17 is began
17 is finished
2 goroutines exist
14 is finished
1 goroutines exist
2 goroutines exist
18 is began
3 goroutines exist
19 is began
16 is finished
2 goroutines exist
3 goroutines exist
20 is began
20 is finished
2 goroutines exist
19 is finished
1 goroutines exist
2 goroutines exist
21 is began
3 goroutines exist
22 is began
18 is finished
2 goroutines exist
3 goroutines exist
23 is began
23 is finished
2 goroutines exist
3 goroutines exist
24 is began
22 is finished
2 goroutines exist
3 goroutines exist
25 is began
21 is finished
2 goroutines exist
24 is finished
1 goroutines exist
2 goroutines exist
26 is began
3 goroutines exist
27 is began
25 is finished
2 goroutines exist
3 goroutines exist
28 is began
26 is finished
2 goroutines exist
3 goroutines exist
29 is began
27 is finished
2 goroutines exist
3 goroutines exist
30 is began
28 is finished
2 goroutines exist
3 goroutines exist
31 is began
30 is finished
2 goroutines exist
3 goroutines exist
32 is began
32 is finished
2 goroutines exist
3 goroutines exist
33 is began
31 is finished
2 goroutines exist
29 is finished
1 goroutines exist
2 goroutines exist
34 is began
3 goroutines exist
35 is began
35 is finished
2 goroutines exist
34 is finished
1 goroutines exist
2 goroutines exist
36 is began
3 goroutines exist
37 is began
33 is finished
2 goroutines exist
3 goroutines exist
38 is began
37 is finished
2 goroutines exist
3 goroutines exist
39 is began
36 is finished
2 goroutines exist
38 is finished
1 goroutines exist
2 goroutines exist
40 is began
3 goroutines exist
41 is began
41 is finished
2 goroutines exist
3 goroutines exist
42 is began
40 is finished
2 goroutines exist
3 goroutines exist
43 is began
39 is finished
2 goroutines exist
3 goroutines exist
44 is began
42 is finished
2 goroutines exist
3 goroutines exist
45 is began
43 is finished
2 goroutines exist
3 goroutines exist
46 is began
44 is finished
2 goroutines exist
3 goroutines exist
47 is began
45 is finished
2 goroutines exist
3 goroutines exist
48 is began
46 is finished
2 goroutines exist
3 goroutines exist
49 is began
48 is finished
2 goroutines exist
3 goroutines exist
50 is began
47 is finished
2 goroutines exist
3 goroutines exist
51 is began
50 is finished
2 goroutines exist
49 is finished
1 goroutines exist
2 goroutines exist
52 is began
3 goroutines exist
53 is began
52 is finished
2 goroutines exist
3 goroutines exist
54 is began
51 is finished
2 goroutines exist
3 goroutines exist
55 is began
55 is finished
2 goroutines exist
3 goroutines exist
56 is began
56 is finished
2 goroutines exist
3 goroutines exist
57 is began
53 is finished
2 goroutines exist
3 goroutines exist
58 is began
57 is finished
2 goroutines exist
54 is finished
1 goroutines exist
58 is finished
0 goroutines exist
1 goroutines exist
59 is began
2 goroutines exist
60 is began
3 goroutines exist
61 is began
59 is finished
2 goroutines exist
3 goroutines exist
62 is began
62 is finished
2 goroutines exist
3 goroutines exist
63 is began
63 is finished
2 goroutines exist
3 goroutines exist
64 is began
61 is finished
2 goroutines exist
60 is finished
1 goroutines exist
2 goroutines exist
65 is began
3 goroutines exist
66 is began
64 is finished
2 goroutines exist
3 goroutines exist
67 is began
66 is finished
2 goroutines exist
3 goroutines exist
68 is began
68 is finished
2 goroutines exist
3 goroutines exist
69 is began
65 is finished
2 goroutines exist
3 goroutines exist
70 is began
67 is finished
2 goroutines exist
3 goroutines exist
71 is began
69 is finished
2 goroutines exist
3 goroutines exist
72 is began
70 is finished
2 goroutines exist
3 goroutines exist
73 is began
73 is finished
2 goroutines exist
3 goroutines exist
74 is began
74 is finished
2 goroutines exist
3 goroutines exist
75 is began
71 is finished
2 goroutines exist
3 goroutines exist
76 is began
72 is finished
2 goroutines exist
75 is finished
1 goroutines exist
2 goroutines exist
77 is began
3 goroutines exist
78 is began
76 is finished
2 goroutines exist
3 goroutines exist
79 is began
78 is finished
2 goroutines exist
3 goroutines exist
80 is began
77 is finished
2 goroutines exist
3 goroutines exist
81 is began
80 is finished
2 goroutines exist
3 goroutines exist
82 is began
81 is finished
2 goroutines exist
3 goroutines exist
83 is began
79 is finished
2 goroutines exist
82 is finished
1 goroutines exist
83 is finished
0 goroutines exist
1 goroutines exist
84 is began
2 goroutines exist
85 is began
3 goroutines exist
86 is began
84 is finished
2 goroutines exist
85 is finished
1 goroutines exist
2 goroutines exist
87 is began
3 goroutines exist
88 is began
86 is finished
2 goroutines exist
3 goroutines exist
89 is began
87 is finished
2 goroutines exist
3 goroutines exist
90 is began
90 is finished
2 goroutines exist
3 goroutines exist
91 is began
88 is finished
2 goroutines exist
3 goroutines exist
92 is began
92 is finished
2 goroutines exist
3 goroutines exist
93 is began
89 is finished
2 goroutines exist
91 is finished
1 goroutines exist
2 goroutines exist
94 is began
3 goroutines exist
95 is began
94 is finished
2 goroutines exist
3 goroutines exist
96 is began
96 is finished
2 goroutines exist
93 is finished
1 goroutines exist
2 goroutines exist
97 is began
3 goroutines exist
98 is began
95 is finished
2 goroutines exist
3 goroutines exist
99 is began
99 is finished
2 goroutines exist
3 goroutines exist
100 is began
98 is finished
2 goroutines exist
97 is finished
1 goroutines exist
100 is finished
0 goroutines exist
```