doujiu7680
2018-10-26 12:46
浏览 49
已采纳

Go中的父子上下文取消顺序

I want to know if there are any guarantees regarding the return order upon Context cancellation in golang.

I want to create a context with cancellation and once all the listeners are done with processing catching and reacting to "<-ctx.Done()" from this context, I want to call os.Exit safely.

A concrete example to explain the idea of what I want is following. I want to catch a signal, trigger all cancellations, and then call os.Exit().

I create a context and listen for a signal:

ctx, cancel := context.WithCancel(context.Background())
go func() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)
    defer signal.Stop(c)

    select {
    case <-c:
        cancel()
    }
}()

In other places I "sign up" for this request several times:

res := NewRes()
go func() {
    <-ctx.Done():
    res.Close()
}()

But then I want to call os.Exit at the point when all the listeners are done.

For that I plan to create either parent or child context like this:

parent, pCancel := context.WithCancel(context.Background())
child, _ := context.WithCancel(parent)
go func() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)
    defer signal.Stop(c)

    select {
    case <-c:
        pCancel()
    case <-child.Done():
        os.Exit(0)
    }
}()

Unfortunately, I did not find the documentation describing the order how context are canceled, so I cannot come up with the correct solution for now.

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • dongshuzui0335 2018-10-26 13:09
    已采纳

    You have to wait all routines before exiting. Calling pCancel() doesn't mean everything will stop. I recommend to do in routine all jobs, but on the main thread to wait for os.Interrupt signal.

    Check example below

    package main
    
    import (
        "context"
        "fmt"
        "os"
        "os/signal"
        "sync"
        "time"
    )
    
    func main() {
        parent, pCancel := context.WithCancel(context.Background())
        child, _ := context.WithCancel(parent)
        wg := &sync.WaitGroup{}
    
        for i := 0; i < 10; i++ {
            go work(wg, child)
        }
    
        c := make(chan os.Signal)
        signal.Notify(c, os.Interrupt)
        defer signal.Stop(c)
    
        select {
        case <-c:
            pCancel()
            fmt.Println("Waiting everyone to finish...")
            wg.Wait()
            fmt.Println("Exiting")
            os.Exit(0)
        }
    }
    
    func work(wg *sync.WaitGroup, ctx context.Context) {
        done := false
        wg.Add(1)
        for !done {
            fmt.Println("Doing something...")
            time.Sleep(time.Second)
            select {
            case <-ctx.Done():
                fmt.Println("Done")
                done = true
            default:
    
            }
        }
        wg.Done()
    }
    

    Although, It's recommended to use principle "Share Memory By Communicating". Here is another example without using WaitGroup.

    package main
    
    import (
        "context"
        "fmt"
        "os"
        "os/signal"
        "time"
    )
    
    func main() {
        parent, pCancel := context.WithCancel(context.Background())
        child, _ := context.WithCancel(parent)
        done := make(chan struct{})
        jobsCount := 10
    
        for i := 0; i < jobsCount; i++ {
            go work(child, done)
        }
    
        c := make(chan os.Signal)
        signal.Notify(c, os.Interrupt)
        defer signal.Stop(c)
    
        select {
        case <-c:
            pCancel()
            fmt.Println("Waiting everyone to finish...")
            for i := 0; i < jobsCount; i++ {
                <-done
            }
            fmt.Println("Exiting")
            os.Exit(0)
        }
    }
    
    func work(ctx context.Context, doneChan chan struct{}) {
        done := false
        for !done {
            fmt.Println("Doing something...")
            time.Sleep(time.Second)
            select {
            case <-ctx.Done():
                fmt.Println("Done")
                done = true
            default:
    
            }
        }
        doneChan <- struct{}{}
    }
    
    打赏 评论

相关推荐 更多相似问题