Search code examples
goconcurrencychannelgoroutine

Goroutines select with range loop


I want to spawn a goroutine to listen to two channels of type chan int and chan os.Signal. I want the behavior to be specific depending on what's received on either channels. Meaning some os.Signal may cause os.exit() and some may not, some int received through chan int may print a statement and some may call a function, so I need this gorountine to be always running since the behaviors are different. And I'd like this to be all handed by one function.

I'm having a hard time figuring out how to achieve this syntactically. It doesn't seem like I can have range loops inside of a select block nor does it seem like I can have a select block inside a range loop. I can't find any resource online on this. Can someone give me an example?


Solution

  • You can put a select statement in a for loop (this is one of the examples in the language spec). Unlike a for...range loop, this will let you read from both channels. It also won't automatically terminate if one of the channels closes. When you receive from a closed channel, a closed channel is always ready to receive and always produces a zero value, and there is a two-valued form of it that tells you whether the channel is open or not.

    Your function might loosely look like

    func HandleStuff(numbers <-chan int, signals <-chan os.Signal) {
        var goingToExit bool
        for {
            select {
            case n := <-numbers:
                if n == 0 {
                    fmt.Printf("zero\n")
                } else if n == 1 {
                    goingToExit = true
                }
            case sig, ok := <-signals:
                if !ok { // the channel is closed
                    return
                } else if goingToExit {
                    os.Exit(0)
                }
            }
        }
    }