Почему все еще идет паника "отправить по закрытому каналу", когда я закрываюсь на стороне отправителя? - PullRequest
0 голосов
/ 10 июля 2019

У меня есть 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()
}

1 Ответ

0 голосов
/ 11 июля 2019

go параллелизм очень мощный. Координационный параллелизм очень жесткий . К счастью, стандартная библиотека go имеет много инструментов, чтобы помочь с этим. Возможно, вам следует ознакомиться с пакетом context.

context.Context используйте готовые каналы под крышками (аналогично вашему stopChan), но есть другие механизмы, такие как цепочка отмены. Они также используются в стандартной библиотеке go для запросов http, базы данных и других блокирующих типов.

Как упоминал @JimB, обычно sync.Once редко требуется при координации производителя / потребителя. chan с и sync.WaitGroup с обычно достаточно.

В любом случае, вот исправление для вашего кода, используя context.Context:

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

...