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("メインゴルーチンを終了します。")
}
-
done := make(chan struct{})
:- 空の構造体の型を持つ
done
チャネルを作成します。このチャネルは、ゴルーチンが処理を完了したことを通知するために使用されます。
- 空の構造体の型を持つ
-
ゴルーチンの起動:
-
go func()
で新しいゴルーチンを起動します。この中で処理を行います。
-
-
time.Sleep(2 * time.Second)
:- この行で、2秒間の処理を模擬します。実際の処理がここに入ります。
-
close(done)
:- 処理が完了したら、
done
チャネルをクローズします。これにより、他のゴルーチン(メインゴルーチンなど)に終了を通知します。
- 処理が完了したら、
-
メインゴルーチンの処理:
- メインゴルーチンでは、
fmt.Println("メインゴルーチンは他の処理を行っています。")
でメッセージを出力し、他の処理を行います。
- メインゴルーチンでは、
-
<-done
:-
done
チャネルからの受信を待ちます。done
チャネルがクローズされると、ここでブロックが解除されます。
-
-
終了メッセージ:
- 最後に、メインゴルーチンが終了することを示すメッセージを出力します。
done
チャンネルパターンは、ゴルーチンの終了を管理するための一般的な手法です。以下に、いくつかの典型的な例を示します。
例1: 複数のゴルーチンが同時に処理を実行する
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: タイムアウトを利用したゴルーチンの終了
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: 複数のゴルーチンによる計算と結果の収集
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
チャネルをクローズします。