Problem with the select statement

Hi,

I have a problem in the use of the select statement in the code below:


package main

import (
    "fmt"
    "time"
)

func GenInts() <-chan int {
    c := make(chan int)
    go func() {
        defer close(c)
        for i:=0; i < 10; i++ {
            c <- i
        }
    }()
    return c
}

func guard(cond bool, c chan<- int) chan<- int {
    if !cond {
        return nil
    } else {
        return c
    }
}

func EvenCheck(ints <-chan int) <-chan int {
    c := make(chan int)
    go func() {
        defer close(c)
        for n := range ints {
            select {
            case guard(n % 2 == 0, c) <- n:
            default:
                continue
            }
            time.Sleep(time.Millisecond)
        }
    }()
    return c
}

func main() {
    for n := range EvenCheck(GenInts()) {
        fmt.Println(n)
    }
}

I want to use the property of the channels to block any writing (or reading also but I do not use it here) if they have the nil value.
Using the guard function, I check if the number is even then I send this number to the output channel.
If not, I proceed with the next integer.

I observed a strange behavior (at least I think so) of the ‘select’ statement.
If I execute this code without introducing a delay in the ‘select’ loop it does not work.
However with a short delay the code works.

Code tested with Go 1.13.4.

Does anyone would have an explanation? …

So, in

select {
    case guard(n % 2 == 0, c) <- n:
    default:
        continue
}

you’re saying “send n to this channel if there is someone ready to ready to receive it, otherwise just continue”. If guard returns nil then there is no-one ready to receive and we will indeed just continue. If guard returns a channel then that channel might have someone ready to receive, but in your case it’s quite likely that the receiver goroutine is busy in the fmt.Println right now and can’t take the call. In that case we again just skip it.

Your delay makes it more likely that the receiver goroutine has had time to print and get back to listening.

In reality this is fragile. A buffered channel helps, but can still overwhelm the receiver. You’re better off not doing the select at all for numbers you don’t want to send and using a blocking send instead of the select.

Thanks you Jakob for your explanations, things seem clearer to me…