LoginSignup
23
18

More than 5 years have passed since last update.

goroutineの実行と、goroutineにおけるsync.WaitGroupの役割

Posted at

goroutineの実行

下記のような
「helloを100 * time.Millisecondごとに5回表示するメソッドsayhelloと、worldを100 * time.Millisecondごとに5回表示するメソッドgoroutine」
があるとする。

main.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("world")
    sayhello("hello")
}
//出力結果
// world
// world
// world
// world
// world
// hello
// hello
// hello
// hello
// hello

これは並行処理ではないため、当然上から順に「sayworldメソッド=>sayhelloメソッド」の順に実行される。
ではGoにおいてこれを並行処理(sayworldメソッドとsayhelloメソッドを同時に実行)するにはどうしたらいいか。

sayworldメソッドの前にgoをつけるだけでいい。

main.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)をコメントアウトしてみる。

main.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を別スレッドで同時実行
    go sayworld("world")
    sayhello("hello")
}
//出力結果
// hello
// hello
// hello
// hello
// hello

すると、並行処理されているはずのsayworldメソッドが実行されず、sayhelloしか実行されていない。
これは、mainスレッドのsayhelloが、「sayworldがgoroutineとして起動=>実行」される前に終了してしまったからである。

スクリーンショット 2019-03-12 21.12.00.png

つまり、「sayworldがgoroutinとして起動=>実行」されるのを待てば良い。
それにはwg sync.WaitGroupを使う

  1. wg.Add(n)でn個の並行処理が存在することを伝え
  2. wg.Wait()で並行処理が終わるまで(wg.Done()で終了を伝える)、処理をやめない
main.go
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が並行処理される

23
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
18