goroutineの実行
下記のような
「helloを100 * time.Millisecondごとに5回表示するメソッドsayhelloと、worldを100 * time.Millisecondごとに5回表示するメソッドgoroutine」
があるとする。
package main
import (
"fmt"
"time"
)
func sayworld(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func sayhello(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
sayworld("world")
sayhello("hello")
}
//出力結果
// world
// world
// world
// world
// world
// hello
// hello
// hello
// hello
// hello
これは並行処理ではないため、当然上から順に「sayworldメソッド=>sayhelloメソッド」の順に実行される。
ではGoにおいてこれを並行処理(sayworldメソッドとsayhelloメソッドを同時に実行)するにはどうしたらいいか。
sayworldメソッドの前にgoをつけるだけでいい。
package main
import (
"fmt"
"time"
)
func sayworld(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func sayhello(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
//sayworldを別スレッドでgoroutineとして同時実行
go sayworld("world")
sayhello("hello")
}
//出力結果
// world
// hello
// hello
// world
// world
// hello
// hello
// world
// world
// hello
このようにGoではメインのスレッドで実行される関数と同時実行したい関数にgoとつけるだけで並行処理ができる。
#sync.WaitGroupの役割
次に以下のようにtime.Sleep(100 * time.Millisecond)をコメントアウトしてみる。
package main
import (
"fmt"
_ "time"
)
func sayworld(s string) {
for i := 0; i < 5; i++ {
// time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func sayhello(s string) {
for i := 0; i < 5; i++ {
// time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
//sayworldを別スレッドで同時実行
go sayworld("world")
sayhello("hello")
}
//出力結果
// hello
// hello
// hello
// hello
// hello
すると、並行処理されているはずのsayworldメソッドが実行されず、sayhelloしか実行されていない。
これは、mainスレッドのsayhelloが、「sayworldがgoroutineとして起動=>実行」される前に終了してしまったからである。
つまり、「sayworldがgoroutinとして起動=>実行」されるのを待てば良い。
それにはwg sync.WaitGroupを使う
- wg.Add(n)でn個の並行処理が存在することを伝え
- wg.Wait()で並行処理が終わるまで(wg.Done()で終了を伝える)、処理をやめない
package main
import (
"fmt"
"sync"
)
//引数にwgのポインタを宣言
func sayworld(s string, wg *sync.WaitGroup) {
//wg.Done()で処理の終了を伝える(deferで関数の最後に実行する)
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func sayhelllo(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
var wg sync.WaitGroup
//wg.Add(n)でwgにn個の並列処理があるということを伝える
wg.Add(1)
//実行したいgoroutineにwgのアドレスを渡す
go sayworld("world", &wg)
sayhelllo("hello")
//「goroutinの処理がwg.Done()されるまで終了しないで」と言うことをwg.Wait()で伝える
wg.Wait()
}
//出力結果
// hello
// hello
// hello
// hello
// hello
// world
// world
// world
// world
// world
このようにすることで、しっかりとmainスレッドと、goroutinが並行処理される