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

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{}{}
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 数字信号处理实验报告
  • ¥15 ensp路由器启动不了一直报#
  • ¥50 安卓10如何在没有root权限的情况下设置开机自动启动指定app?
  • ¥15 ats2837 spi2从机的代码
  • ¥200 wsl2 vllm qwen1.5部署问题
  • ¥100 有偿求数字经济对经贸的影响机制的一个数学模型,弄不出来已经快要碎掉了
  • ¥15 数学建模数学建模需要
  • ¥15 已知许多点位,想通过高斯分布来随机选择固定数量的点位怎么改
  • ¥20 nao机器人语音识别问题
  • ¥15 怎么生成确定数目的泊松点过程