Go

How do you close a channel safely when there are multiple senders?

Question 242HardGo 1.22 to 1.25

The rule is that only a sender closes a channel, and only when no other sender can still send. Sending on a closed channel panics, closing twice panics, and a receiver cannot tell whether senders are finished.

With multiple senders:

  • N senders, 1 receiver: a coordinator waits on a WaitGroup and then closes the channel.
  • Receiver wants to stop the senders: do not close the data channel. Signal on a separate done channel or cancel a context, and let the senders select on it.
func produce(ctx context.Context, n int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	for id := range n {
		wg.Go(func() {
			for i := 0; ; i++ {
				select {
				case out <- id*1000 + i:
				case <-ctx.Done():
					return
				}
			}
		})
	}
	go func() { wg.Wait(); close(out) }() // the single closer
	return out
}

What does this print?

ch := make(chan int, 2)
ch <- 1
close(ch)
v, ok := <-ch
fmt.Println(v, ok) // 1 true   (buffered values drain first)
v, ok = <-ch
fmt.Println(v, ok) // 0 false

Avoid "safe close" wrappers built on recover or sync.Once. They hide an ownership bug instead of fixing it.

More on Concurrency Patterns & sync

All 38 Concurrency Patterns & sync questions