Reputation: 2106
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
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
Reputation: 44646
The select
statement blocks until any one of the case
s 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