A for-select loop repeatedly waits for channel communication. Use it when a goroutine must handle work as it arrives but also stop when it receives a control signal. Unlike `for msg := range channel`, the `select` can wait on multiple channels; after a case runs, the loop waits again. If multiple cases are ready, Go chooses one at random. section 9.5.2
Listing 9.26 explore.go: Stop listening to new messages
func (s *Solver) listenToBranches() {
wg := sync.WaitGroup{}
defer wg.Wait()
for {
select {
case <-s.quit: #1
log.Println("the treasure has been found, stopping worker")
return
case p := <-s.pathsToExplore: #2
wg.Add(1)
go func(p *path) {
defer wg.Done()
s.explore(p)
}(p)
}
}
}
A maze solver listens for new paths and launches an exploration goroutine for each one. Receiving on `quit` returns from the listener; a deferred wait keeps it from finishing before its explorers do. section 9.5.2
A `select` can also choose between sending work and receiving a stop signal. Put the send in its own case rather than checking `quit` with a `default` case and then sending: the stop signal could arrive between that check and the send. A `default` case is useful when the loop has other work to do instead of waiting. section 9.5.2
Listing 9.33 animation.go: Implementing registerExploredPixels
// registerExploredPixels registers positions as explored on the image,
// and, if we reach a threshold, adds the frame to the output GIF.
func (s *Solver) registerExploredPixels() {
const totalExpectedFrames = 30
explorablePixels := s.countExplorablePixels() #1
pixelsExplored := 0
for {
select {
case <-s.quit: #2
return
case pos := <-s.exploredPixels: #3
s.maze.Set(pos.X, pos.Y, s.palette.explored) #4
pixelsExplored++
if pixelsExplored%(explorablePixels/totalExpectedFrames) == 0 {
s.drawCurrentFrameToGIF()
}
}
}
}
The same pattern drives animation: receive an explored pixel, paint it, and periodically add a GIF frame, or return when `quit` is received. Here the loop performs repeated processing, not just repeated goroutine launches. section 9.6.2
The pattern also fits request-processing loops that should exit when a request is canceled. Keep an exit case alongside the communication that advances the work. section 9.7
