Go 並列処理の基本概念
1. 基本的な goroutine
func main() {
// 普通の関数呼び出し(同期的)
sayHello("同期")
// goroutine で実行(非同期的)
go sayHello("非同期1")
go sayHello("非同期2")
go sayHello("非同期3")
// メイン goroutine が終わると全部終わるため、待つ
time.Sleep(1 * time.Second)
}
func sayHello(name string) {
for i := 0; i < 3; i++ {
fmt.Printf("%s: Hello %d\n", name, i)
time.Sleep(200 * time.Millisecond)
}
}
2. WaitGroup を使った同期
WaitGroup は「全部の goroutine が終わるまで待つ」ための道具。 Add でカウント増やして、Done で減らして、Wait で待つ。これめっちゃ使う!
func main() {
var wg sync.WaitGroup
// 3つの goroutine を起動
for i := 1; i <= 3; i++ {
wg.Add(1) // カウンターを増やす
go func(id int) {
defer wg.Done() // 終わったらカウンターを減らす
fmt.Printf("Worker %d: 処理開始\n", id)
time.Sleep(time.Duration(id) / 2 * time.Second)
fmt.Printf("Worker %d: 処理完了\n", id)
}(i) // iを引数として渡すの大事!
}
wg.Wait() // 全部のgoroutineが終わるまで待つ
}
3. Channel を使った通信
Channel は goroutine 間でデータをやり取りする仕組み。<-の矢印で送受信するんだけど、慣れると直感的。
func main() {
// チャネルを作成
ch := make(chan string)
// データを送信する goroutine
go func() {
time.Sleep(1 * time.Second)
ch <- "Hello from goroutine!" // チャネルに送信
}()
// データを受信(ブロックする)
msg := <-ch
fmt.Println("受信:", msg)
}
4. Buffered Channel
func main() {
// バッファサイズ3 のチャネル
ch := make(chan int, 3)
// バッファがあるので、すぐにブロックしない
ch <- 1
ch <- 2
ch <- 3
fmt.Println("受信:", <-ch)
fmt.Println("受信:", <-ch)
fmt.Println("受信:", <-ch)
}
5. 実践的な例:並列で API っぽい処理
func main() {
type Result struct {
ID int
Value string
Err error
}
tasks := []int{1, 2, 3, 4, 5}
results := make(chan Result, len(tasks))
// 各タスクを並列実行
for _, id := range tasks {
go func(taskID int) {
// 何か重い処理をシミュレート
time.Sleep(time.Duration(taskID*100) * time.Millisecond)
results <- Result{
ID: taskID,
Value: fmt.Sprintf("Task %d completed", taskID),
Err: nil,
}
}(id)
}
// 結果を収集
for i := 0; i < len(tasks); i++ {
result := <-results
fmt.Printf("結果: ID=%d, Value=%s\n", result.ID, result.Value)
}
}
6. select を使った複数チャネルの待機
select は複数のチャネルを同時に待てるやつ。どれか一つでも準備できたら処理が進む。
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "ch1からのメッセージ"
}()
go func() {
time.Sleep(500 * time.Millisecond)
ch2 <- "ch2からのメッセージ"
}()
// どちらか早い方を受信
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("受信:", msg1)
case msg2 := <-ch2:
fmt.Println("受信:", msg2)
}
}
}