Goで複数の仕事があるとして、goroutineを新たに作成してそれに1つ1つの仕事を任せるのは普通のやり方だと思う。たとえばクライアントからの接続を待っているサーバをGoで書いているとして、クライアントからのリクエストごとにgoroutineを起動してその後のやりとりを任せるといったようなパターンは普通だ。
そういったパターンでリクエストが多いと、どんどんgoroutineが起動してしまってメモリが足りなくなることがある。そういった場合に、goroutineの起動を待たせて、最大でもある一定数以上はgoroutineが並行して走っていないようにしたいことはよくあるのだが、そのためにはどうしたらよいだろうか?
一般的なやり方はチャネルを「残りいくつのgoroutineを起動してよいか」という値(セマフォ)として使う方法である。
チャネルを作成するときにチャネルのバッファサイズを決めることができる。バッファが一杯になるとそのチャネルへの送信はブロックされる(それを実行しているgoroutineがその行で実行が停止してチャネルに空きができるまで先に進まなくなる)。この性質をうまく使うと、チャネルのバッファサイズと同じ個数だけgoroutineを起動することができるのである。
下に例を示す。
c := make(chan bool, 10) // 最大でgoroutineは10個
for /* なにかをする */ {
c <- true // もしcが一杯ならこの行で待たされる
go func() {
// cから読みだしてその値を捨てる -- ほかのgoroutineのための空きを作る
defer func() { <-c }()
// このgoroutineが行うことをする
}()
}
上記のプログラムではgoroutineは最大でも10個しか並列には走らない。このようにチャネルを使って最大数を制御すると、無秩序にgoroutineが起動するという問題を解決できる。