I have a stopChan
to notify senders to close channel and a sync.Once
to make sure only one sender can close the channel, but I still get a "send on closed channel" panic, why?
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
",i)
close(intChan)
})
fmt.Println("End. [sender] %id", i)
}(i)
}
sum := 0
for e := range intChan {
fmt.Printf("Receive %d
", e)
sum += e
if sum > 10 {
close(stopChan)
fmt.Printf("Got %d
", sum)
break
}
}
fmt.Println("End. [receiver]")
wg.Wait()
}
go
concurrency is very powerful. Coordinating concurrency is very hard. Fortunately the go
standard library has many tools to help with this. You should probably familiarize yourself with the context
package.
context.Context
use done-channels under the covers (similar to your stopChan
) but have other mechanisms like cancelation chaining. They are also used throughout the go
standard library for http, database and other blocking-type requests.
As @JimB has mentioned, usually sync.Once
are rarely needed when coordinating producer/consumers. chan
s and sync.WaitGroup
s are usually enough.
Anyway, here's the coordination fix to your code using context.Context
: