#並行処理
アドベントカレンダー4日目は並列処理についてです。3日目はこちら
Goにおける並行処理
Goは並行処理ととても相性がよく、最大の特徴の一つといってもいいでしょう。
次に並列処理のプログラムとその実行例を見てみます。
プログラムは0から4までの数字を2倍にして表示するというものです。
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup, results chan<- int) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
results <- id * 2
}
func main() {
const numWorkers = 5
var wg sync.WaitGroup
results := make(chan int, numWorkers)
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, results)
}
wg.Wait()
close(results)
for result := range results {
fmt.Printf("Result: %d\n", result)
}
}
$ go run app/day4/main.go
Worker 4 starting
Worker 0 starting
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 3 done
Worker 4 done
Worker 0 done
Worker 1 done
Worker 2 done
Result: 6
Result: 8
Result: 0
Result: 2
Result: 4
もう一度実行してみます。
go run app/day4/main.go
Worker 4 starting
Worker 2 starting
Worker 3 starting
Worker 1 starting
Worker 0 starting
Worker 1 done
Worker 0 done
Worker 4 done
Worker 3 done
Worker 2 done
Result: 2
Result: 0
Result: 8
Result: 6
Result: 4
1度目の実行と2度目の実行で値が異なっています。
Goにおける並行処理は非同期に行われ、実行順番が保証されないので注意が必要です。
実行時間を計測してみます。
$ time go run app/day4/main.go
Worker 4 starting
Worker 0 starting
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 3 done
Worker 0 done
Worker 4 done
Worker 1 done
Worker 2 done
Result: 6
Result: 0
Result: 8
Result: 2
Result: 4
real 0m1.119s
user 0m0.079s
sys 0m0.095s
time.Sleep(time.Second)
を5度呼んでいるため、逐次処理ならば5秒かかるはずですが、実際は1.1秒ほどしかかかっていません。このように、待ちが発生しているようなプログラムにおいて並行処理はとても有効です。