この問題には2つの部分があります。
最初に、親ゴルーチンが停止しても、子どものすべてが通知を受けて停止するように、何らかの方法で停止させる必要があります。
一方、親は、完了するまで子どもを待つ必要があります。さもなければ、いくつかのgoroutinesが正しく終了する前に、goroutineから戻ったり、アプリケーションから終了したりするでしょう。
わかりやすくするために、エラー処理、タイムアウトなどの実装は無視しています。
context.Context
を使用すると、実行コンテキスト処理ツールの優れた階層が得られ、2番目の問題を解決するために、sync.WaitGroup
を使用すると、ゴルーチンのグループがタスクを完了するのを待つことができます。簡単なデモは、次のようになります。
[ info ] level2
[ info ] level2
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level1
[ info ] level2
[ info ] level2
現在context.Context
とsync.WaitGroup
を組み合わせた機能を提供する公式パッケージはありません。のようないくつかの出力が私たちに与え
func main() {
all := &sync.WaitGroup{}
rootCtx, rootCancel := context.WithCancel(context.Background())
all.Add(1)
go level1(rootCtx, all)
// just to simulate stop, we could use an os signal instead
// app ends after 3 seconds
go func() {
time.Sleep(time.Second * 3)
rootCancel()
}()
all.Wait()
}
func level1(parent context.Context, all *sync.WaitGroup) {
defer all.Done()
l1Ctx, l1Cancel := context.WithCancel(parent)
defer l1Cancel()
for i := 0; i < 3; i++ {
all.Add(1)
go level2(l1Ctx, all)
}
for {
select {
case <-parent.Done():
return
// other cases if any,
// this is a sample
case <-time.After(time.Second):
log.Println(`level1`)
}
}
}
func level2(parent context.Context, all *sync.WaitGroup) {
defer all.Done()
for {
select {
case <-parent.Done():
return
case <-time.After(time.Second):
log.Println(`level2`)
}
}
}
。一番近いのはerrgroup
で、この機能に似ています。
これに対してブール値を使用している場合、なぜ2つのチャネルを作成していますか? – Gant