

現在golangを勉強しており、特にgoroutine、context、cancel()、channelなどを色々試しています。

ふと、goroutineからgoroutineを起動してもよいのか？と思い、以下のようにmain()からgoroutineでparent()を実行し、さらにその中でgoroutineでchild()を実行するサンプルを作成してみました。  
※child()を実行する条件は、pop()から取得できる値が1の場合のみとし、それ以外の場合は前のchild()を実行し続ける。次に1がきた場合は前のchild()を終了し新しいchild()を起動する…みたいなちょっと複雑な感じにしています。

[code]

    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
    }
    
[/code]

ここで以下2点について質問させてください。

１．そもそもgoroutineからgoroutineを起動してもよいものなのでしょうか？  
(documentを探しているのですが、見当たらなく…)

２．上記をPlaygroundで実行すると結果は出力されますが、go vet?により以下の警告がでます。

[code]

    ./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
    
[/code]

原因は記述のとおり「childCancel()が実行されないことがあるよ！」といったことだと思うのですが、これを回避する方法はございますでしょうか？

乱文で恐縮ですが、よろしくお願いいたします。


CC BY-SA 4.0
2019-08-22T15:53:55.193
57552
2019-08-22T15:53:55.193
35566
question
go goroutine
Goのgoroutineとcancelについて教えてください