Go by Example: WaitGroups

複数のゴルーチンの完了を待つために、WaitGroup を使うことができます。

package main
import (
    "fmt"
    "sync"
    "time"
)

これは、すべてのゴルーチンで実行する関数です。

func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)

時間のかかるタスクをシミュレートするためにスリープします。

    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}
func main() {

この WaitGroup は、ここで起動したすべてのゴルーチンが完了するのを 待つために使われます。WaitGroup を関数に引数として渡す場合は、 ポインタで 渡す必要がある点に注意してください。

    var wg sync.WaitGroup

いくつかのゴルーチンを起動し、そのたびに WaitGroup のカウンターを インクリメントします。

    for i := 1; i <= 5; i++ {
        wg.Add(1)

ゴルーチンのクロージャで同じ i の値が再利用されてしまうのを防ぎます。 詳細は、FAQ を参照してください。

        i := i

クロージャでワーカー呼び出しをラップし、ワーカーが完了したことを WaitGroup に伝えます。こうすると、ワーカー自身は並行処理のことを 意識する必要がなくなります。

        go func() {
            defer wg.Done()
            worker(i)
        }()
    }

WaitGroup のカウンターが 0 に戻るまで(つまり、すべてのワーカーが 完了したと通知してくるまで)ブロックします。

    wg.Wait()

このアプローチは、ワーカーからエラーを簡単に伝える方法がない点に 注意してください。より発展的なユースケースのためには、 errgroup パッケージ の利用を検討してください。

}
$ go run waitgroups.go
Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 4 done
Worker 1 done
Worker 2 done
Worker 5 done
Worker 3 done

ワーカーが起動および終了する順序は、実行するたびに変わりえます。

Next example: Rate Limiting.