在选择中支持一种通信(chan)

我知道,如果有多个“通信”可以在select声明中进行,则随机选择一个。我正在尝试找到一种替代方法,它可以更喜欢一种“交流”而不是另一种。


背景是我正在使用上下文杀死的通道上的 go-routine 中发送值。当我杀死它时,我希望立即关闭通道,但目前代码有时会在关闭之前在通道上发送最终值。


这是代码的简化版本:


   ctx, cancel := context.WithCancel(context.Background())

   ch := make(chan int)


   go func() {

      defer close(ch)

      for i := 1; ; i++ {

         select {

         case <-ctx.Done():

            return

         case ch <- i:

         }

      }

   }()


   print(<-ch)

   print(<-ch)

   cancel()

   print(<-ch)

   print(<-ch)

这有时会打印 1200,但通常会打印 1230。 在操场上尝试一下


关于如何重组代码以支持第一种情况有什么想法吗?(即让它总是打印 1200。)


慕桂英3389331
浏览 103回答 2
2回答

波斯汪

这似乎是不可能的,因为cancel()它不是主 goroutine 中的阻塞操作。正因为如此,当select解除阻塞时,可能会有多种情况可用,并且没有办法让一个渠道优于另一个渠道。任何类型的 check-channel-then-write 方案都将是活泼的,因为在检查后可以取消上下文。使用done通道并写入它而不是上下文取消将起作用,因为写入done通道将是主 goroutine 的阻塞操作,并且 select 将始终有一个活动案例。

幕布斯6054654

请注意,这是一个更新的答案,因为原始答案存在问题。正如其他人所指出的,如果没有额外的同步,您将无法避免竞争条件。您可以使用 Mutex,但sync.Cond看起来很合适。在下面的代码中,接收 goroutine 表示它已从 chan 接收到值。它在发出信号之前取消上下文(使用Cond.Signal),发送 goroutine 等待信号。这避免了竞争条件,因为上下文状态在被检查之前已更新。ctx, cancel := context.WithCancel(context.Background())ch := make(chan int)cond := sync.NewCond(&sync.Mutex{}) // *** newgo func() {&nbsp; &nbsp; defer close(ch)&nbsp; &nbsp; cond.L.Lock()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// *** new&nbsp; &nbsp; defer cond.L.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// *** new&nbsp; &nbsp; for i := 1; ; i++ {&nbsp; &nbsp; &nbsp; &nbsp; ch <- i&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// *** moved&nbsp; &nbsp; &nbsp; &nbsp; cond.Wait()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// *** new&nbsp; &nbsp; &nbsp; &nbsp; if ctx.Err() != nil {&nbsp; &nbsp; &nbsp; &nbsp;// *** changed&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}()print(<-ch)cond.Signal()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // *** newprint(<-ch)cond.Signal()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // *** newprint(<-ch)cancel()cond.Signal()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // *** newprint(<-ch)cond.Signal()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // *** new这是我能看到的最简单的方法,接收 goroutine 在取消上下文后将不会在通道上接收任何值。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go