Go

How do you safely close a channel that might be closed by multiple goroutines?

Question 189HardGo 1.22 to 1.25

First, restructure the code if you can, because multiple closers usually means ownership isn't clear. When you genuinely need a "stop" signal that several parties may trigger, wrap the close in sync.Once:

type Stopper struct {
	once sync.Once
	ch   chan struct{}
}

func NewStopper() *Stopper { return &Stopper{ch: make(chan struct{})} }

func (s *Stopper) Stop()                 { s.once.Do(func() { close(s.ch) }) }
func (s *Stopper) Done() <-chan struct{} { return s.ch }

context.cancelCtx uses the same idea internally: under a mutex it checks whether it is already cancelled and closes its lazily created done channel only the first time, so cancel() is safe to call repeatedly.

Anti-patterns interviewers want you to reject:

  • defer func(){ recover() }() around close. It works, but it hides design bugs and is racy.
  • Checking "is it closed?" with a non-blocking receive before closing. This is a TOCTOU race: another goroutine can close it between the check and your close.

Only close a signal channel from multiple places. Never close a data channel that other goroutines are still sending on.

More on Channels & select

All 38 Channels & select questions