Go言語におけるgoroutineのdoneチャンネルパターン





```go
package main

import (
	"fmt"
	"time"
)

func main() {
	done := make(chan struct{}) // 空の構造体のチャネルを作成

	go func() {
		// 何らかの処理を行う
		time.Sleep(2 * time.Second) // 2秒間スリープ
		fmt.Println("ゴルーチンの処理が完了しました。")
		close(done) // 処理が完了したらdoneチャネルをクローズ
	}()

	// ここで他の処理ができる
	fmt.Println("メインゴルーチンは他の処理を行っています。")

	// doneチャネルがクローズされるのを待つ
	<-done // doneチャネルから受信
	fmt.Println("メインゴルーチンを終了します。")
}
```



1. **`done := make(chan struct{})`**:
   - 空の構造体の型を持つ `done` チャネルを作成します。このチャネルは、ゴルーチンが処理を完了したことを通知するために使用されます。

2. **ゴルーチンの起動**:
   - `go func()` で新しいゴルーチンを起動します。この中で処理を行います。

3. **`time.Sleep(2 * time.Second)`**:
   - この行で、2秒間の処理を模擬します。実際の処理がここに入ります。

4. **`close(done)`**:
   - 処理が完了したら、`done` チャネルをクローズします。これにより、他のゴルーチン（メインゴルーチンなど）に終了を通知します。

5. **メインゴルーチンの処理**:
   - メインゴルーチンでは、`fmt.Println("メインゴルーチンは他の処理を行っています。")` でメッセージを出力し、他の処理を行います。

6. **`<-done`**:
   - `done` チャネルからの受信を待ちます。`done` チャネルがクローズされると、ここでブロックが解除されます。

7. **終了メッセージ**:
   - 最後に、メインゴルーチンが終了することを示すメッセージを出力します。

`done` チャンネルパターンは、ゴルーチンの終了を管理するための一般的な手法です。以下に、いくつかの典型的な例を示します。

### 例1: 複数のゴルーチンが同時に処理を実行する

```go
package main

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

func worker(id int, wg *sync.WaitGroup, done chan struct{}) {
	defer wg.Done() // 処理が完了したらワーカーを減らす

	// 何らかの処理を行う
	time.Sleep(time.Duration(id) * 1 * time.Second)
	fmt.Printf("ワーカー %d の処理が完了しました。\n", id)

	// 処理が完了したらdoneチャネルをクローズ
	select {
	case done <- struct{}{}:
	default: // チャネルが既にクローズされた場合は何もしない
	}
}

func main() {
	var wg sync.WaitGroup
	done := make(chan struct{}) // 空の構造体のチャネルを作成

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go worker(i, &wg, done) // 各ゴルーチンを起動
	}

	go func() {
		wg.Wait() // 全てのワーカーが完了するのを待つ
		close(done) // 全てのワーカーが完了したらdoneチャネルをクローズ
	}()

	<-done // doneチャネルから受信
	fmt.Println("全ての処理が完了しました。")
}
```

### 説明
- **ワーカー**: 各ゴルーチンが処理を行い、処理が完了したら `done` チャネルに通知します。
- **`sync.WaitGroup`**: 全てのゴルーチンが完了するのを待つために使用します。
- **チャネルのクローズ**: 全てのワーカーが完了した後に `done` チャネルをクローズします。

### 例2: タイムアウトを利用したゴルーチンの終了

```go
package main

import (
	"fmt"
	"time"
)

func longRunningTask(done chan struct{}) {
	time.Sleep(5 * time.Second)
	fmt.Println("長い処理が完了しました。")
	close(done) // 処理が完了したらdoneチャネルをクローズ
}

func main() {
	done := make(chan struct{})
	go longRunningTask(done)

	select {
	case <-done:
		fmt.Println("処理が完了しました。")
	case <-time.After(2 * time.Second):
		fmt.Println("タイムアウトしました。")
	}
}
```

### 説明
- **長い処理**: ゴルーチンで5秒間スリープします。
- **`select`文**: `done` チャネルがクローズされるのを待つか、2秒後にタイムアウトします。
- **タイムアウト**: タイムアウトが発生した場合の処理を行います。

### 例3: 複数のゴルーチンによる計算と結果の収集

```go
package main

import (
	"fmt"
	"sync"
)

func calculate(id int, results chan<- int, done chan struct{}) {
	// 何らかの計算を行う
	result := id * id // 簡単な計算
	results <- result // 結果を送信
	close(done) // 処理が完了したらdoneチャネルをクローズ
}

func main() {
	results := make(chan int)
	done := make(chan struct{})
	var wg sync.WaitGroup

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			calculate(id, results, done)
		}(i)
	}

	go func() {
		wg.Wait()
		close(done) // 全てのゴルーチンが完了したらdoneチャネルをクローズ
	}()

	// 結果を収集
	go func() {
		for result := range results {
			fmt.Println("計算結果:", result)
		}
	}()

	<-done // doneチャネルから受信
	fmt.Println("全ての計算が完了しました。")
}
```

### 説明
- **計算ゴルーチン**: 各ゴルーチンが計算を行い、その結果を `results` チャネルに送信します。
- **結果の収集**: 結果を受信するゴルーチンが `results` チャネルから結果を出力します。
- **`sync.WaitGroup`**: 全ての計算が完了するのを待ち、`done` チャネルをクローズします。