Help us understand the problem. What is going on with this article?

sync.WaitGroupの正しい使い方

More than 5 years have passed since last update.

Goroutineを複数使って並列で処理を行って、それがすべて完了したら次に進みたいとしよう。Goroutineの完了はそれを生成したgoroutineに通知されるわけではないので、メインのgoroutineは何らかのメカニズムを使って全員が完了するまで待って、全員が完了したら実行を再開する必要がある。

sync.WaitGroupは複数のgoroutineの完了を待つための値だ(Javaを知っていれば、java.util.concurrent.CountDownLatchによく似ている)。WaitGroupの値に対してメソッドWaitを呼ぶと、WaitGroupが0になるまでWaitはブロックされる(待たされる)。従って、やりたい処理の数だけWaitGroupの値をインクリメントしておいて、処理完了時にデクリメントすれば、Waitを呼んで処理完了を待っているメインのgoroutineは、すべての処理が完了する(カウンタが0に戻る)まで待って、処理完了時に再開されるというわけだ。

というわけで概念的には単純なWaitGroupだが、マルチスレッドプログラミング特有の落とし穴があるので注意されたい。ありがちな間違いは、下のコードのように、新たに起動したgoroutineの中でWaitGroupをインクリメントしてしまうことだ。

wg := &sync.WaitGroup{}  // WaitGroupの値を作る
for i := 0; i < 10; i++ { // (例として)10回繰り返す
    go func() {
        wg.Add(1)  // wgをインクリメント
        // ここで何か処理を行う
        wg.Done()  // 完了したのでwgをデクリメント
    }()
}
wg.Wait()  // メインのgoroutineはサブgoroutine 10個が完了するのを待つ

上のプログラムは一見正しそうにみえる。Goroutineを複数起動しているが、子goroutineはまずwgをインクリメントして、完了時にデクリメントしているので、親のメインのgoroutineはすべての子が完了するまで待たされるはずだ ―― そうだよね?

しかし実際にはそうではない。Goroutineがスケジューリングされるタイミングは任意なので、goroutineが10個生成されて、しかしまだどれもまったく走っていないという状態がありえる。その状態でメインのgoroutineが最後の行に到達すると、wgは0(初期値)のままなので、メインのgoroutineはそのまま下に抜けてしまう。

このバグを直すには次のようにすればよい。いつ走るかわからないgoroutineの中でwgをインクリメントするのではなく、goroutineを起動する前にwgをインクリメントすればよいのである。

wg := &sync.WaitGroup{}  // WaitGroupの値を作る
for i := 0; i < 10; i++ { // (例として)10回繰り返す
    wg.Add(1)  // wgをインクリメント
    go func() {
        // ここで何か処理を行う
        wg.Done()  // 完了したのでwgをデクリメント
    }()
}
wg.Wait()  // メインのgoroutineはサブgoroutine 10個が完了するのを待つ

このようにすると最後の行は確実にすべてのgoroutineの完了を待つことになる。マルチスレッドプログラミングは意外な落とし穴があるが、これもその一つなので注意されたい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした