【问题标题】:Why Go channel is blocking but not quitting为什么 Go 频道被阻塞但不退出
【发布时间】:2021-11-08 05:20:18
【问题描述】:

我有这两个版本来使用 signal.NotifyContext 对信号实现上下文消除

版本 1 https://play.golang.org/p/rwOnYEgPecE

func main() {
    ch := run()
    <-ch
}

func run() chan bool {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
    var done = make(chan bool)

    //go func() {
    select {
    case <-ctx.Done():
        fmt.Println("Quitting")
        stop()
        done <- true
    }
    //}()
    return done
}

版本 2 https://play.golang.org/p/oijbICeSrNT

func main() {
    ch := run()
    <-ch
}

func run() chan bool {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
    var done = make(chan bool)

    go func() {
       select {
       case <-ctx.Done():
          fmt.Println("Quitting")
          stop()
          done <- true
       }
    }()
    return done
}

为什么第一个版本打印Quitting行但不退出,而第二个版本打印并正常退出?

【问题讨论】:

  • 在操场上,他们都取消并退出。你的操作系统是什么,去版本,去环境?
  • @mh-cbon 为避免平台 CPU 使用率过高,playground 内置了最大执行时间限制,如果程序运行时间过长会自动终止。
  • @colm.anseo 是的,也许,但在这两种情况下都通过&lt;-context.Done() 案例,因此,他们都在退出前取消。
  • 第一种情况的问题是done频道没有缓冲
  • 但无论如何,只要关闭它,它就可以了play.golang.org/p/hY7RmVVTfOu

标签: go channels


【解决方案1】:

第一种情况没有按预期运行的原因是因为一切都在单个(主)goroutine 中运行。

select {
   case <-ctx.Done():
      fmt.Println("Quitting")
      stop()
      done <- true  // this blocks because no one is listening
   }
}

在您的第二个示例中,因为上述逻辑在 goroutine 中运行,所以 main() 将在频道上监听。

第二种方法是首选,因为任何信号处理程序 - 在程序的生命周期内运行 - 都应该在其自己的 goroutine 中运行。

【讨论】:

  • 代码注释确实是这里的关键原因。
  • 第一个代码:执行例程 x 阻塞在通道中写入,没有人可以阅读。第二:执行例程 x 块,执行例程 y 读取,x 解除阻塞,x 退出,y 退出。
  • @colm.anseo 我接受了答案。谢谢 UmNyobe。但是关闭频道对第一个版本有何帮助?
  • 关闭通道是非阻塞操作。它通常用作指示某事“完成”的机制 - 因为任何读取操作都会解除阻塞。
猜你喜欢
  • 2022-01-02
  • 2020-04-23
  • 2016-09-23
  • 1970-01-01
  • 2017-01-26
  • 2014-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多