sync.WaitGroup
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 64/107。
「Goroutines Basics」レッスンでは、ゴルーチンが終了するまで待つために time.Sleep を使用しました。これは脆弱なアプローチです。sync.WaitGroup は、goroutines の集合が作業を complete するまで待つための適切な方法を提供します。
WaitGroup は counter のように機能します。goroutine を開始する before に increment し、goroutine が完了したときに decrement し、counter が 0 に達するまで block します:
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // カウンターをインクリメント
go func(id int) {
defer wg.Done() // 完了時にデクリメント
fmt.Printf("Worker %d finished\n", id)
}(i)
}
wg.Wait() // カウンターが0になるまでブロック
fmt.Println("All workers complete")
}3つの主要なメソッドは、counter を n 増加させる Add(n)、counter を1減少させる(Add(-1)と同等)Done()、そして counter がゼロになるまでブロックする Wait() です。defer wg.Done() を使用すると、goroutine がパニックになった場合でも counter が減少します。
よくある間違いは、Add を goroutine の中で呼び出し、その前に呼び出さないことです。これにより、すべての goroutine が登録される前に Wait が戻る可能性がある競合状態が発生します。
// 間違い - レースコンディション
go func() {
wg.Add(1) // Wait() の後に実行される可能性がある
defer wg.Done()
}()
// 正しい - 起動前に Add
wg.Add(1)
go func() {
defer wg.Done()
}()WaitGroupsは、concurrentな操作をcoordinationするためにstructに埋め込まれることが多く、Goでスレッドセーフな型を構築するうえで不可欠です。
チャレンジ
簡単sync.WaitGroupを使用して、複数の同時実行される file downloads を調整する download manager を構築しましょう。manager は、任意の sleep timer に頼らず、すべての downloads が complete したタイミングを追跡します。
コードを2つのファイルに分けて整理します。
downloader.go: download の coordination logic を定義します。ID(int)、Filename(string)、Size(KBを表すint)フィールドを持つDownloadstruct を Create します。DownloadにProcess(wg *sync.WaitGroup)method を Implement します。この method は、file size に基づく Duration(Size * 10milliseconds を使用)だけ sleep することで downloading をシミュレートします。download が complete したら、Downloaded: [Filename] ([Size]KB)を print します。WaitGroup の counter が正しく decrement されるように、defer wg.Done()を使用します。すべての downloads を goroutines を使用して concurrently に launch し、すべての downloads が complete するまで待機する function
StartDownloads(downloads []Download) intを Create します。各 goroutine を launch する前にwg.Add(1)を call し、goroutine の内部では call しないようにしてください。すべての downloads の Size を合計した total size を return します。main.go: download information を読み取り、concurrent downloads を調整します。downloads の数を読み取り、続いて各 download の ID、filename、size を読み取ります。downloads を Create し、
StartDownloadsに渡します。すべての downloads が complete した後、All downloads complete: [total]KB totalを print します。
次の inputs が提供されます。
- Line 1: downloads の数(integer)
- Following lines: 各 download について3行 — ID(integer)、filename(string)、size(KB単位のinteger)
例えば、次の入力が与えられた場合:
3
1
report.pdf
20
2
image.png
5
3
data.csv
10output には downloads が complete する順序(sleep time が短いため、小さい file が先に終了します)が表示され、その後に summary が続きます。
Downloaded: image.png (5KB)
Downloaded: data.csv (10KB)
Downloaded: report.pdf (20KB)
All downloads complete: 35KB totaltime.Sleep を使用する場合との key difference は、wg.Wait() が、すべての goroutines が Done() で completion を signal するまで block することです。これにより、各 download にかかる時間に関係なく、正確な synchronization が得られます。
自分で試してみよう
package main
import (
"fmt"
)
func main() {
// ダウンロード数を読み取る
var n int
fmt.Scanln(&n)
// ダウンロード情報を読み取る
downloads := make([]Download, n)
for i := 0; i < n; i++ {
var id int
var filename string
var size int
fmt.Scanln(&id)
fmt.Scanln(&filename)
fmt.Scanln(&size)
downloads[i] = Download{
ID: id,
Filename: filename,
Size: size,
}
}
// TODO: downloads スライスで StartDownloads を呼び出す
// TODO: Print the summary: "All downloads complete: [total]KB total"
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ