Goのgoroutineとcancelについて教えてください
現在golangを勉強しており、特にgoroutine、context、cancel()、channelなどを色々試しています。
ふと、goroutineからgoroutineを起動してもよいのか?と思い、以下のようにmain()からgoroutineでparent()を実行し、さらにその中でgoroutineでchild()を実行するサンプルを作成してみました。
※child()を実行する条件は、pop()から取得できる値が1の場合のみとし、それ以外の場合は前のchild()を実行し続ける。次に1がきた場合は前のchild()を終了し新しいchild()を起動する…みたいなちょっと複雑な感じにしています。
package main
import (
"context"
"fmt"
"time"
)
var (
slices = []int{1, 2, 1, 2, 2, 2, 1, 2, 1}
)
func main() {
fmt.Println("main start")
ctx, cancel := context.WithCancel(context.Background())
defer func() {
cancel()
time.Sleep(2 * time.Second)
}()
go parent(ctx)
time.Sleep(4 * time.Second)
fmt.Println("main end")
}
func parent(ctx context.Context) {
fmt.Println("parent start")
i := 0
var childCtx context.Context
var childCancel context.CancelFunc
for {
select {
case <-ctx.Done():
fmt.Println("parent cancel")
return
default:
fmt.Println("parent process")
i += 1
v := pop()
switch v {
case 1:
if childCancel != nil {
childCancel()
}
childCtx, childCancel = context.WithCancel(ctx)
go child(childCtx, i)
default:
fmt.Printf("not start child: %d\r\n", i)
}
time.Sleep(1000 * time.Millisecond)
}
}
}
func child(ctx context.Context, i int) {
fmt.Printf("child start: %d\r\n", i)
for {
select {
case <-ctx.Done():
fmt.Printf("child cancel: %d\r\n", i)
return
default:
fmt.Printf("child process: %d\r\n", i)
time.Sleep(100 * time.Millisecond)
}
}
}
func pop() int {
var i int
if len(slices) > 0 {
i = slices[0]
slices = slices[1:]
}
return i
}
ここで以下2点について質問させてください。
1.そもそもgoroutineからgoroutineを起動してもよいものなのでしょうか?
(documentを探しているのですが、見当たらなく…)
2.上記をPlaygroundで実行すると結果は出力されますが、go vet?により以下の警告がでます。
./prog.go:49:5: the childCancel function is not used on all paths (possible context leak)
./prog.go:39:4: this return statement may be reached without using the childCancel var defined on line 49
原因は記述のとおり「childCancel()が実行されないことがあるよ!」といったことだと思うのですが、これを回避する方法はございますでしょうか?
乱文で恐縮ですが、よろしくお願いいたします。