[Go]Goの並列処理入門：Goroutineとsync.WaitGroupを理解しよう!

## はじめに

Go（Golang）は並行処理が得意なプログラミング言語であり、その強力な特徴の一つに**Goroutine**があります。
さらに、複数のGoroutineを管理し、処理が完了するまで待機するための**sync.WaitGroup**という仕組みも提供されています。

少し自分もわかりにくい概念があったので知識の整理も兼ねて、Goroutineとsync.WaitGroupを初心者向けにわかりやすく解説していこうと思います。

---

## Goroutineとは？

Goroutine（ゴルーチン）は、Goにおける並行処理の基本単位です。スレッドのように動作しますが、**非常に軽量**であり、大量のGoroutineを作成してもシステムのリソースを圧迫しません。

**使い方**

Goroutineは、`go` キーワードを関数の前に付けるだけで簡単に作成できます。

#### **Goroutineの基本的なコード**
```go
package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello from Goroutine!")
}

func main() {
    go sayHello() // Goroutineを起動
    
    fmt.Println("Hello from main!")
    
    time.Sleep(time.Second) // Goroutineの実行を待つためにスリープ
}
```

**実行結果**
```
Hello from main!
Hello from Goroutine!
```

Goroutineは並行して実行されるため、`main()` 関数が終了すると、他のGoroutineも終了してしまいます。
そのため、`time.Sleep()` を使って少し待機することで、Goroutineの動作を確認できます（実際の開発では`sync.WaitGroup`を使う方が推奨されます。若干処理速度がタイミングによって変わることがありました）

---

## sync.WaitGroupとは？

Goroutineを複数起動した場合、それらの処理が**すべて終了するまで待機したい**ことがあります。そのために使うのが`sync.WaitGroup`です。

**sync.WaitGroupの使い方**

1. `Add(n)` を呼び出して、待つGoroutineの数を指定する
2. 各Goroutine内で `Done()` を呼び出す
3. `Wait()` を呼び出すことで、すべてのGoroutineが終了するまで待機する

#### **sync.WaitGroupを使ったサンプルコード**
```go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 処理が完了したらカウントを減らす
    fmt.Printf("Worker %d: started\n", id)
    time.Sleep(time.Second) // 擬似的な重い処理
    fmt.Printf("Worker %d: finished\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 待つGoroutineの数を増やす
        go worker(i, &wg)
    }
    
    wg.Wait() // すべてのGoroutineが完了するまで待つ
    fmt.Println("All workers are done!")
}
```

### **実行結果**
```
Worker 1: started
Worker 2: started
Worker 3: started
Worker 1: finished
Worker 2: finished
Worker 3: finished
All workers are done!
```

Goroutineは並行して動作するため、実行順は毎回変わることがあります。

---

## Goroutineとsync.WaitGroupの活用例

複数のウェブサイトからデータを取得する場合を考えてみましょう。Goroutineを使うことで高速に処理できます。

#### **ウェブサイトを並行してリクエストするサンプルコード**
```go
package main

import (
    "fmt"
    "net/http"
    "sync"
)

func fetchURL(url string, wg *sync.WaitGroup) {
    defer wg.Done()
    
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("Error fetching:", url, err)
        return
    }
    fmt.Println("Fetched:", url, "Status:", resp.Status)
}

func main() {
    var wg sync.WaitGroup
    urls := []string{
        "https://golang.org",
        "https://example.com",
        "https://github.com",
    }
    
    for _, url := range urls {
        wg.Add(1)
        go fetchURL(url, &wg)
    }
    
    wg.Wait()
    fmt.Println("All requests completed!")
}
```

### **実行結果（例）**
```
Fetched: https://golang.org Status: 200 OK
Fetched: https://example.com Status: 200 OK
Fetched: https://github.com Status: 200 OK
All requests completed!
```

このように、Goroutineを使うことで複数のHTTPリクエストを並列に実行でき、処理時間を大幅に短縮できます。

---

## まとめ

| 概念 | 説明 |
|------|------|
| **Goroutine** | 軽量な並行処理の単位。`go` キーワードで簡単に作成可能 |
| **sync.WaitGroup** | 複数のGoroutineの終了を待機する仕組み |
| **活用例** | 複数のウェブリクエストや並列処理のタスクを高速に処理 |

Goroutineを使いこなすことができればできるタスクの幅も広がるな〜と感じました。
マルチプロセスは考え方に馴染むまで時間がかかる印象があるけど頑張ってマスターしたい...