【问题标题】:Why still Go panic "send on closed channel" when I close on sender side?当我在发送方关闭时,为什么仍然会恐慌“在关闭的通道上发送”?
【发布时间】:2019-07-11 05:27:41
【问题描述】:

我有一个stopChan 来通知发件人关闭频道,还有一个sync.Once 来确保只有一个发件人可以关闭频道,但我仍然收到“在关闭的频道上发送”的恐慌,为什么?

func muitiSenderClose() {
    const SenderNum = 3

    wg := sync.WaitGroup{}
    wg.Add(SenderNum)

    intChan := make(chan int)

    stopChan := make(chan struct{})

    once := sync.Once{}
    for i := 0; i < SenderNum; i++ {
        go func(i int) {
            defer wg.Done()
            needStop := false
            for {
                select {
                case <-stopChan:
                    needStop = true
                case intChan <- 1:
                case intChan <- 2:
                case intChan <- 3:
                }

                if needStop {
                    break
                }
            }
            once.Do(func() {
                fmt.Printf("%d want to close chan\n",i)
                close(intChan)
            })
            fmt.Println("End. [sender] %id", i)
        }(i)
    }
    sum := 0
    for e := range intChan {
        fmt.Printf("Receive %d\n", e)
        sum += e
        if sum > 10 {
            close(stopChan)
            fmt.Printf("Got %d\n", sum)
            break
        }
    }
    fmt.Println("End. [receiver]")

    wg.Wait()
}

【问题讨论】:

  • 所有发件人完成后关闭通道。您需要sync.Once 来关闭频道这一事实应该表明它位于错误的位置。您还有数据竞赛,请务必检查竞赛检测器。
  • 你有多个 goroutines 都在 intChan 上发送。他们不能都关闭它。共享频道的发件人需要等到所有发件人发送完毕才能关闭。

标签: go channel


【解决方案1】:

go 并发功能非常强大。协调并发是非常 困难。幸运的是,go 标准库有很多工具可以帮助解决这个问题。您可能应该熟悉 context 包。

context.Context 在幕后使用 done-channels(类似于您的 stopChan),但具有其他机制,例如取消链接。它们还在go 标准库中用于 http、数据库和其他阻塞类型的请求。

正如@JimB 所提到的,在协调生产者/消费者时,通常很少需要sync.Oncechans 和 sync.WaitGroups 通常就足够了。

无论如何,这是使用 context.Context 对您的代码进行的协调修复:

https://play.golang.org/p/QwszE_bW41X

【讨论】:

    猜你喜欢
    • 2022-11-11
    • 2016-04-26
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 2011-03-22
    相关资源
    最近更新 更多