【问题标题】:Understanding graceful channel close in Go理解 Go 中的优雅通道关闭
【发布时间】:2019-04-17 00:13:46
【问题描述】:

在来自 Go101 站点的 this article 中,我已经阅读了一些关于 stopCh 频道双重选择的技巧(在“2.一个接收者,N 个发送者,唯一的接收者通过关闭附加信号说“请停止发送更多”渠道”)。

您能否描述一下它是如何工作的?我真的需要在实际应用中使用它吗?

UPD:我没有询问有关频道关闭的问题。我已经询问了这部分代码的用法:

        // The try-receive operation is to try
        // to exit the goroutine as early as
        // possible. For this specified example,
        // it is not essential.
        select {
        case <- stopCh:
            return
        default:
        }

        // Even if stopCh is closed, the first
        // branch in the second select may be
        // still not selected for some loops if
        // the send to dataCh is also unblocked.
        // But this is acceptable for this
        // example, so the first select block
        // above can be omitted.
        select {
        case <- stopCh:
            return
        case dataCh <- rand.Intn(Max):
        }

双重选择stopCh的真正用例是什么?

【问题讨论】:

  • 在实际应用中肯定会用到,是否需要使用取决于用例。它的工作原理是这样的:tour.golang.org/concurrency/4
  • 如我所见,您没有理解我的问题。我要求双重选择频道,而不是近距离通话。
  • 给予停止优先权。在第二次选择中,如果同时有工作和停止信号,则随机选择一个。

标签: go channels


【解决方案1】:

这里的关键是了解如果多个情况可以继续进行时 select 的行为方式,即伪随机:

  1. 如果一个或多个通信可以继续,则通过统一的伪随机选择选择一个可以继续的通信。

https://golang.org/ref/spec#Select_statements

select {
case <- stopCh:
    return
case dataCh <- rand.Intn(Max):
}

仅使用第二条 select 语句,在关闭 stopCh 后,如果以下至少一项为真,则两种情况都可能继续进行:

  1. dataCh 已缓冲,容量不足
  2. 即使在 stopCh 已关闭之后,至少有一个 goroutine 尝试从 dataCh 接收数据

如果不明确检查stopCh,有可能(尽管不太可能)运行时重复选择第二种情况,即使预期goroutine 会退出。如果 goroutine 碰巧在每次迭代中发射导弹,您就会发现这可能是个问题。

如果可以肯定地排除这两个条件,则可以省略第一个 select 语句,因为这两种情况都准备好继续进行是不可能的。 Go101 文章只是展示了一个保证有效的解决方案,无需做任何假设。

这种模式在实际代码中并不少见,通常与上下文取消有关:

func f(ctx context.Context, ch chan T) {
    for {
        // Make sure we don't shoot after ctx has been
        // canceled, even if a target is already lined up.
        select {
        case <-ctx.Done():
            return
        default:
        }

        // Or, equivalently: if ctx.Err() != nil { return }


        select {
        case <-ctx.Done():
            return
        case t := <-ch:
            launchMissileAt(t)
        }
    }
}

【讨论】:

    猜你喜欢
    • 2016-01-29
    • 2021-06-16
    • 2022-11-17
    • 2012-08-08
    • 2016-08-09
    • 2017-12-31
    • 2018-02-05
    • 2014-02-05
    • 1970-01-01
    相关资源
    最近更新 更多