Goルーチンとは
スレッドよりも小さいGo特有の処理単位。goではこのGoルーチンを生成し、並行処理を実行できる。ゴルーチンとも言う。
Goルーチンの使用方法
go 関数名
の形で実行すればその関数はGoルーチンとなる。
sync.WaitGroup
Goルーチンは完了しても特に何の通知もなく終了してしまう。
そこで終了を検知する仕組みが必要。それがsync.WaitGroup。
WaitGroupの値に対してWait()を呼ぶと、WaitGroupが0になるまで待ってくれる。
以下のように使う。
mainの書き方
func main() {
var wg sync.WaitGroup
wg.Add(Goルーチンの数)
go Goルーチン(&wg)
wg.Wait()
}
関数の書き方
func 関数(wg *sync.WaitGroup) {
処理
wg.Done()
}
ポイント
- まず最初に
sync.WaitGroup
をwg
という変数に入れる。 -
wg.Add()
で実行したいGoルーチンの関数の数を指定する。これはつまり、WaitGroupにカウンタをセットしていて、このカウンタが0になると全てのGoルーチンが終了したという見なされる。 - 関数内では処理が終了した段階で
wg.Done()
を実行する。これによりWaitGroupのカウンタが-1される。 -
wg.Wait()
はカウンタが0になるまで待ってくれる。
Goルーチンの実行速度検証
以下のようなファイルを用意し、Goルーチンを使用した場合とそうでない場合の速度を比較する。loopNumbers()
では数をloopLetters()
では英文字を10回ループする。それをstandardFunc()
では普通に逐次実行、goFunc()
ではGoルーチンを利用して実行する。
package main
import "time"
func loopNumbers() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
}
}
func loopLetters() {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
}
}
func standardFunc() {
loopNumbers()
loopLetters()
}
func goFunc() {
go loopNumbers()
go loopLetters()
}
func main() {
}
以下のようにベンチマーク測定をする。BenchmarkFunc()が逐次実行の時間を測定しており、BenchmarkGoFunc()の方はGoルーチンの速度を測定している。
package main
import "testing"
func BenchmarkFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
standardFunc()
}
}
func BenchmarkGoFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
goFunc()
}
}
実行結果は以下のようになる。
$ go test -run x -bench .
goos: darwin
goarch: amd64
pkg: first_webapp
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkFunc-8 12172 117629 ns/op
BenchmarkGoFunc-8 629256 2174 ns/op
PASS
ok first_webapp 3.919s
ポイント
- Goルーチンを使用したほうが54倍、速くなっていることがわかる。