goroutine不尊重`ctx.done()`或正确退出

时间:2019-03-30 00:07:15

标签: go

当用户按下Ctrl-C时,我试图正常退出。我正在尝试Make Ctrl+C cancel the context.Context中的代码。     包主

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {

    ctx := context.Background()

    // trap Ctrl+C and call cancel on the context
    ctx, cancel := context.WithCancel(ctx)
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    defer func() {
        signal.Stop(c)
        cancel()
        fmt.Println("Cleaned up")
    }()
    go func() {
        select {
        case <-c:
            fmt.Println("Got interrupt signal")
            cancel()
        case <-ctx.Done():
        }
        fmt.Println("Stopped monitoring")
    }()
    select {
    case <-ctx.Done():
        fmt.Println("notified to quit")
    case <-time.NewTimer(time.Second * 2).C:
        fmt.Println("done something")
    }
}

当用户按下Ctrl-c时,它可以正常工作,它可以控制以下各项:

Got interrupt signal
Stopped monitoring
notified to quit
Cleaned up

但是,如果正常退出,则无法正常工作,如下所示:

done something
Cleaned up

我的意思是它应该打印出Stopped monitoring,但不是。在defer cleanup函数中,它调用了cancel(),这应该触发select in monitoring goroutine退出,但不会。

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

感谢@Zan Lynx,我制定了以下解决方案。

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    terminated := monitor(ctx, cancel)
    defer func() {
        cancel()
        fmt.Println("Cleaned up")
        <-terminated // wait for the monior goroutine quit
    }()
    select {
    case <-ctx.Done():
        fmt.Println("notified to quit")
    case <-time.NewTimer(time.Second * 1).C:
        fmt.Println("done something")
    }
}

func monitor(ctx context.Context, cancel context.CancelFunc) <-chan interface{} {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    terminated := make(chan interface{})
    go func() {
        defer close(terminated)
        defer fmt.Println("Stopped monitoring1")
        defer signal.Stop(c)
        select {
        case <-c:
            fmt.Println("Got interrupt singnal")
            cancel()
        case <-ctx.Done():
        }
    }()
    return terminated
}