Reflection
Reflection

Reputation: 2106

Selecting from buffered vs non-buffered channel

When I'm running the following simple code it prints "ping" as expected:

    messages := make(chan string)

    go func() { messages <- "ping" }()
    fmt.Println(<-messages)

However, when I'm using the same non-buffered channel with select, it's not fulfilling it with a ping, thus printing "no message sent":

    messages := make(chan string)

    select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }

Why this is happening? The channels are the same, but it's accessible with goroutine but not with select.

In addition, I've found out that when I'm converting it to a buffered channel (with size 1) it fulfills the channel like a charm: Why?

    messages := make(chan string,1)

    select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }

Note nothing is hanging up, but just immediately returns as I've described.

Upvotes: 4

Views: 4046

Answers (2)

OZahed
OZahed

Reputation: 493

go needs some sort of "checkpoints" (blocking statements or timeout in other goroutines) to switch the executing goroutine when you use the Unbuffered channel type channels will be blocked until something reads it, when the main go-routine enters the select statement, select blocks until there is some activity(message) or release happens in one of the cases and since your case read is not done yet, channel blocks because unbuffered channels need both read and write to act as checkpoint, so selects jumps to default statement. when you use buffered channels, any activity will send a signal over the channel but it won't be blocked until the buffer is full. so when buffered channels are used, there is no need for the reader to be blocked, so select detects a data to work with on the channel.

also in

messages := make(chan string)
go func() { messages <- "ping" }()
fmt.Println(<-messages)

you waited on the channel with <-messages read

No reads on channel keeps Unbuffered channel, blocked:

messages := make(chan string)
select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }

buffered channels do not block so write action will be preformed:

messages := make(chan string,1)
select {
case messages <- "ping":
    fmt.Println("sent message")
default:
    fmt.Println("no message sent")
}

Upvotes: 1

blackgreen
blackgreen

Reputation: 44646

The select statement blocks until any one of the cases is ready, or it runs the default case if none is ready.

In your second program, no receive operation appears before select, so the case messages <- "ping" is not ready — there is no receiver —, and default is always executed.

With a buffered channel, the send operation does not block even if there's no receiver on the other end, so case messages <- "ping" is ready and runs.

In the snippet with the goroutine, the send operation runs concurrently, so the main program flow can move on to the fmt.Println call and block on <-messages, until the concurrent send makes a value available on the channel

Upvotes: 6

Related Questions