了解Go中的正常频道关闭

时间:2019-04-16 13:49:21

标签: go channels

在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的真正用例是什么?

1 个答案:

答案 0 :(得分:2)

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

  
      
  1. 如果可以进行一种或多种通信,则可以通过统一的伪随机选择来选择可以进行的单个通信。
  2.   

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

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

仅使用第二个select语句,在stopCh关闭之后,如果满足以下至少一项要求,则两种情况都可能继续进行:

  1. dataCh已缓冲且未达到容量
  2. 即使dataCh已关闭,至少有一个goroutine尝试从stopCh接收

如果没有明确检查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)
        }
    }
}