Go言語チャネルガイド

チャネルについての学習備忘録まとめです。

### ステップ1：goroutineの基本概念
Go言語の並行処理は**goroutine**から始まります。goroutineは軽量スレッドで、OSスレッドより高速でメモリ効率に優れています。

```go
package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 3; i++ {
        time.Sleep(300 * time.Millisecond)
        fmt.Println(i)
    }
}

func main() {
    go printNumbers() // goroutineで起動
    go func() { // 無名関数のgoroutine
        fmt.Println("並行処理中...")
    }()
    
    time.Sleep(1 * time.Second)
    fmt.Println("メイン終了")
}
/* 出力例（順不同）:
並行処理中...
1
2
3
メイン終了
*/
```

**goroutineの特徴**:
- 起動コストが低い（2KB程度のスタック）
- スケジュールはGoランタイムが管理
- `go`キーワードで簡単に起動可能

### ステップ2：チャネル登場の必然性
goroutine間で安全にデータを共有するには？→ **共有メモリの問題点**:

```go
// 共有メモリ方式（非推奨）
var counter int

func increment() {
    for i := 0; i < 1000; i++ {
        counter++ // データ競合が発生！
    }
}

func main() {
    go increment()
    go increment()
    time.Sleep(1 * time.Second)
    fmt.Println(counter) // 2000にならない可能性
}
```

**チャネル解決の哲学**:  
"*Do not communicate by sharing memory; instead, share memory by communicating.*"  
（メモリ共有で通信するな、通信でメモリを共有せよ）

### ステップ3：チャネルの本質的理解
チャネルは3つの重要な特性を持ちます：

1. **順序保証キュー構造**
   ```go
   ch := make(chan int, 3)
   ch <- 1; ch <- 2; ch <- 3
   fmt.Println(<-ch) // 1（常に最初の値）
   ```

2. **並行アクセス安全機構**
   ```go
   // 100個のgoroutineから同時アクセス
   for i := 0; i < 100; i++ {
       go func(n int) {
           ch <- n // 安全に送信
       }(i)
   }
   ```

3. **自動ブロッキング機構**
   ```go
   go func() {
       ch <- "data" // 受信されるまでブロック
   }()
   fmt.Println(<-ch) // 送信されるまでブロック
   ```

### ステップ4：基本操作とバッファの概念
#### チャネルの作成と操作
```go
// バッファなし（同期型）
unbuffered := make(chan int)

// バッファあり（非同期型）
buffered := make(chan string, 5)

// 送受信操作
buffered <- "hello" // 送信
msg := <-buffered   // 受信
close(buffered)     // クローズ
```

#### バッファなし vs バッファあり
| 特徴             | バッファなし             | バッファあり               |
|------------------|--------------------------|----------------------------|
| 送信動作         | 受信までブロック         | バッファ空きがあれば即時   |
| 受信動作         | 送信までブロック         | データがあれば即時         |
| デッドロックリスク | 高い                     | 低い                       |
| ユースケース     | 厳密な同期が必要な場合   | 非同期処理、一時保管       |

**動作イメージ**:  
```mermaid
graph LR
    A[送信goroutine] -- データ --> B[バッファなしチャネル]
    B -- 即時転送 --> C[受信goroutine]
    
    D[送信goroutine] -- データ --> E[バッファありチャネル]
    E --> F[バッファ]
    F --> G[受信goroutine]
```

### ステップ5：実践パターン集

#### パターン1: データ生成パイプライン
```go
func generateData(n int) <-chan int {
    out := make(chan int)
    go func() {
        for i := 0; i < n; i++ {
            out <- i
        }
        close(out)
    }()
    return out
}

func main() {
    for data := range generateData(5) {
        fmt.Println(data) // 0,1,2,3,4
    }
}
```

#### パターン2: ワーカープール
```go
func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d: 処理中 %d\n", id, job)
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)
    
    // 3つのワーカー起動
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    
    // ジョブ投入
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)
    
    // 結果収集
    for r := 1; r <= 5; r++ {
        fmt.Println("結果:", <-results)
    }
}
```

#### パターン3: タイムアウト処理
```go
func fetchData() chan string {
    ch := make(chan string)
    go func() {
        time.Sleep(3 * time.Second)
        ch <- "データ"
    }()
    return ch
}

func main() {
    select {
    case data := <-fetchData():
        fmt.Println("成功:", data)
    case <-time.After(1 * time.Second):
        fmt.Println("タイムアウト")
    }
}
```

### ステップ6：システムプログラミングでの応用
低レベルアクセスでは、チャネルのブロッキング特性がハードウェアイベントの待機に最適です。

```go
func watchHardware() <-chan string {
    eventCh := make(chan string)
    go func() {
        for {
            // ハードウェア状態の監視（擬似コード）
            time.Sleep(500 * time.Millisecond)
            if rand.Float32() > 0.8 {
                eventCh <- "イベント発生"
            }
        }
    }()
    return eventCh
}

func main() {
    hwEvent := watchHardware()
    for i := 0; i < 3; i++ {
        fmt.Println("イベント待機中...")
        fmt.Println(<-hwEvent) // イベントまでブロック
    }
}
```

### ステップ7：高度なパターン

#### パイプライン処理
```go
func producer(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    // パイプライン: 生成 → 二乗
    for res := range square(producer(1, 2, 3)) {
        fmt.Println(res) // 1, 4, 9
    }
}
```

#### コンテキストとの連携
```go
func worker(ctx context.Context, ch chan int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("終了通知受信")
            return
        case data := <-ch:
            fmt.Println("処理:", data)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    ch := make(chan int)
    
    go worker(ctx, ch)
    
    ch <- 1
    ch <- 2
    
    cancel() // ワーカーに終了通知
    time.Sleep(100 * time.Millisecond)
}
```

### ステップ8：ベストプラクティスと落とし穴

#### ゴールデンルール
1. **所有権の原則**: チャネル作成者がクローズ責任を持つ
   ```go
   func createChan() <-chan int {
       ch := make(chan int)
       go func() {
           defer close(ch) // 作成者がクローズ
           // 処理...
       }()
       return ch
   }
   ```

2. **デッドロック防止**:
   - バッファサイズの適切な設定
   - selectでdefaultケースを使用
   ```go
   select {
   case ch <- data:
   default: // 送信不可時の代替処理
   }
   ```

3. **リソースリーク防止**:
   ```go
   // 終了シグナルチャネルを使用
   done := make(chan struct{})
   go func() {
       defer close(done)
       // 処理...
   }()
   
   select {
   case <-done:
   case <-time.After(time.Second):
   }
   ```

#### パフォーマンスチューニング
| シナリオ               | 推奨方法                |
|------------------------|-------------------------|
| 高頻度通信            | バッファありチャネル    |
| 厳密な同期            | バッファなしチャネル    |
| 大量データ転送        | スライスをポインタで送信 |
| 終了管理              | context.Context         |

### ステップ9：チャネル哲学の実践的応用
チャネルは単なる技術ではなく、Goの設計哲学そのものです。

**従来の共有メモリ方式**:
```go
var counter int
var mutex sync.Mutex

func increment() {
    mutex.Lock()
    counter++ // 危険な共有メモリアクセス
    mutex.Unlock()
}
```

**チャネル方式**:
```go
func counterManager() chan int {
    ch := make(chan int)
    go func() {
        count := 0
        for {
            count += <-ch // 通信で状態変更
        }
    }()
    return ch
}

func main() {
    counterCh := counterManager()
    counterCh <- 1 // 安全なインクリメント
}
```

### まとめ：チャネル習得の4段階

1. **基本操作**: 作成/送受信/クローズの習得
2. **動作理解**: バッファなし/ありの違いの体感
3. **パターン適用**: ワーカープールやパイプラインの実装
4. **哲学の体現**: 「通信によるメモリ共有」の実践