I am new to golang and have a hard time understanding how the channels work. My understanding is that by default, channels are supposed to be blocking, so I expect a goroutine that writes into a channel to be frozen by the scheduler until an other goroutine reads the channel content. So I tried the following code, which gives the corresponding output:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var v int
func main() {
ch := make(chan string)
wg.Add(2)
go func(ch chan string) {
fmt.Println("Ready to receive")
for msg := range ch {
fmt.Println("received: ", msg)
fmt.Println(v)
}
wg.Done()
}(ch)
go func(ch chan string) {
fmt.Println("Will send the SMS to mama")
ch <- "msg 1"
v += 1
fmt.Println("Done! sent the message 1")
ch <- "msg 2"
v += 1
fmt.Println("Done! sent the message 2")
ch <- "msg 3"
v += 1
fmt.Println("Done! sent the message 3")
close(ch)
wg.Done()
}(ch)
wg.Wait()
}
output:
Will send the SMS to mama
Ready to receive
received: msg 1
0
Done! sent the message 1
Done! sent the message 2
received: msg 2
2
received: msg 3
2
Done! sent the message 3
I am a bit surprised as I was expecting the following order:
msg 1 sent
msg 1 received
msg 2 sent
msg 2 received
and so on. But this is obviously not the case.
Does someone have any idea on why Go behaves this way? Many thanks,
Here is a link to the code https://play.golang.org/p/O6SXf0CslPf. And here are my sources for stating what I said earlier: https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb https://rakyll.org/scheduler/
This behaviour is completely normal so to answer your question
so I expect a goroutine that writes into a channel to be frozen by the scheduler until an other goroutine reads the channel content
Schedular may or may not continue that same goroutine after value is sent through channel unless further synchronization is needed.
So for example After "msg 2" is sent to ch and is read in another goroutine in the following line
ch <- "msg 2"
goroutine can continue to execute v += 1 and call fmt.Println before other goroutine calls it.
Also calls to fmt.Println from different goroutines requires synchronization and possibly mutex calls which may also reorder print statements.
Further more there is data race on variable v
Related
package main
import (
"fmt"
)
func write(ch chan int) {
for i := 0; i < 5; i++ {
fmt.Println("avaliable", i)
ch <- i
fmt.Println("successfully wrote", i, "to ch")
}
close(ch)
}
func main() {
ch := make(chan int)
go write(ch)
for v := range ch {
fmt.Println("read value", v, "from ch")
}
}
Output
avaliable 0
successfully wrote 0 to ch
avaliable 1
read value 0 from ch
read value 1 from ch
successfully wrote 1 to ch
avaliable 2
successfully wrote 2 to ch
avaliable 3
read value 2 from ch
read value 3 from ch
successfully wrote 3 to ch
avaliable 4
successfully wrote 4 to ch
read value 4 from ch
Since this is a unbuffered channel it should have blocked as soon as data is written into it until another goroutine reads from the same channel. But it accepts data beyond it's capacity.
Intended Behavior
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 5; i++ {
fmt.Println("avaliable", i)
ch <- i
fmt.Println("successfully wrote", i, "to ch")
}
close(ch)
}
func main() {
ch := make(chan int)
go write(ch)
time.Sleep(time.Second)
for v := range ch {
fmt.Println("read value", v, "from ch")
time.Sleep(time.Second)
}
}
Output
avaliable 0
read value 0 from ch
successfully wrote 0 to ch
avaliable 1
read value 1 from ch
successfully wrote 1 to ch
avaliable 2
read value 2 from ch
successfully wrote 2 to ch
avaliable 3
read value 3 from ch
successfully wrote 3 to ch
avaliable 4
read value 4 from ch
successfully wrote 4 to ch
If some timers are placed throughout the code so that the main goroutine is blocked before each iteration it works as expected.
You can't infer anything from your output, because there is no ordering guarantee between the "read value" and "successfully wrote" Printfs executing. (There is one between the "available" Printf, which occurs before the channel send, and the "read value" Printf, which occurs after the corresponding channel receive, and you can see that that ordering is never violated in your output).
The channel isn't buffering anything, because it has no buffer; it's just that the two different goroutines run in an indeterminate order after the channel send completes. Sometimes the sending side gets to go first and prints the "successfully wrote" message, and sometimes the receiving side gets to go first and prints the "read value" message. Neither one ever "gets ahead" by more than one value, because they're still fully synchronized on the channel send; they're just racing to print their status messages immediately after.
When you add the Sleep calls to main, it just so happens to make it so that the goroutine running write will always be blocked on waiting to send the next value, while the one running main blocks on the call to Sleep. When the timer expires, the main goroutine wakes up, and immediately finds that it has something waiting for it on the channel, grabs it, and goes back to sleep, and then the write goroutine gets woken up. By slowing things down you've gotten the scheduler to run things in a consistent order (although it's still partly a matter of luck); without the sleeps, with everything running as fast as it can, the result is apparently random.
If that were the case, the output of the "sleeping" version would be even more chaotic.
You cannot expect a coherent output from two independent threads even if they are synchronized at some point.
case1
package main
func main() {
dogChan := make(chan int)
dogChan <- 1
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/xuzhongwei/Source/awesomeProject/main.go:5 +0x50
case2
package main
func main() {
dogChan := make(chan int)
go func(ch chan int) {
}(dogChan)
dogChan <- 1
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/xuzhongwei/Source/awesomeProject/main.go:9 +0x72
case3
package main
func main() {
dogChan := make(chan int)
go func(ch chan int) {
<- ch
}(dogChan)
dogChan <- 1
}
case4
package main
func main() {
dogChan := make(chan int)
go func(ch chan int) {
<- ch
}(dogChan)
dogChan <- 1
dogChan <- 2
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/xuzhongwei/Source/awesomeProject/main.go:10 +0x90
case5
package main
func main() {
dogChan := make(chan int)
go func(ch chan int) {
for {
select {
case <- ch:
}
}
}(dogChan)
dogChan <- 1
dogChan <- 2
dogChan <- 3
dogChan <- 4
dogChan <- 5
}
Could anyone tell me why case1, case2 got errors while case3 is ok?
In case1 my guess is that dogChan is not used in goroutine so it is treated to be closed.
In case2 my guess is that although dogChan is passed in goroutine but it is not used in goroutine so it is treated to be closed
Could anyone tell me why case4 got errors while case5 is ok?
Why do you think thats happening in case1 and case2? The channels are meant to behave as a synchronisation primitive between a sender and receiver. You have a sender sending on a channel, dogChan but none is receiving on it.
Without a receiving goroutine or a receive operation on a goroutine, the sender simply blocks (being a unbuffered channel)
Same problem on case4, you have two sends on the channel, but a single receive on the goroutine. The dogChan <- 2 will block forever. In case5, if your intention was to read from the channel, simply use a range loop to iterate over the successive values sent over it.
Golang expects program to read message(s) placed into channel.
Consumer (reader) needs to drain (read) all messages from channel, either using simple for-read, or for-select. Channel send and receive both block until sender and receiver are ready.
case1, case2 = send one message to channel, block awaiting reader, read zero messages
case4 = send one message to channel, block awaiting reader, reader does not consume (read) message
case3 = send one message to channel, consume one message from channel, sender blocks awaiting reader
case5 = send five messages to channel, consume all (five) messages, each send blocks until reader receives
// for range over channel
for msg := range ch {
// process msg
}
// for select
done := false
for !done {
select {
case msg := <-ch: {
// process msg
}
case ch == nil: {
done = true
}
}
}
// producer should close channel
close(ch)
Note:
channel can be buffered, specify a channel (queue) size
channel size default = 1 (unbuffered), writer blocks when channel full
I am expecting this code to go in an infinite loop, sending and receiving the message. But it does not seem to either send or receive. Why?
go func() {
for {
select {
case ch1 <- 1:
println("sent 1 ")
case c := <-ch1:
println(" received ", c)
}
time.Sleep(time.Second)
}
}()
The channel documentation says:
If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready.
The select documentation says:
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.
The receive case cannot proceed because there's not a ready sender. The send case cannot proceed because there's not a ready receiver. The program deadlocks in the select.
A single goroutine cannot make the send and receive both ready because the goroutine can only execute one branch of the select at a time.
The program will loop forever if the channel is buffered (playground example).
To unbuffered channel with one goroutine: No (Why: by design):
You may use Buffered channel with one goroutine or
two goroutines using unbuffered channel to avoid deadlock:
If the channel is unbuffered, the sender blocks until the receiver has
received the value. If the channel has a buffer, the sender blocks
only until the value has been copied to the buffer; if the buffer is
full, this means waiting until some receiver has retrieved a value.
1- Using buffered channel: The main itself is a goroutine and if you need do it in one goroutine you should use buffered channel like this:
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int, 1)
for {
select {
case ch1 <- 1:
fmt.Println("sent 1 ")
case c := <-ch1:
fmt.Println(" received ", c)
}
time.Sleep(time.Second)
}
}
output:
sent 1
received 1
sent 1
received 1
sent 1
received 1
2- Using unbuffered channel: You may send and receive to unbuffered channel in two case statements in one select, and you need two goroutines here, see:
Try this:
package main
import (
"fmt"
"time"
)
func main() {
go ct("Alex")
go ct("John")
//select {}
time.Sleep(300 * time.Millisecond)
}
func ct(name string) {
for {
select {
case ch1 <- 1:
fmt.Println(name, "sent 1")
case c := <-ch1:
fmt.Println(name, "received:", c)
}
fmt.Println(name, "waiting...")
time.Sleep(100 * time.Millisecond)
}
}
var ch1 = make(chan int)
output:
John sent 1
John waiting...
Alex received: 1
Alex waiting...
John received: 1
John waiting...
Alex sent 1
Alex waiting...
John sent 1
John waiting...
Alex received: 1
Alex waiting...
Here is an example:
func main() {
c := make(chan int)
i := 0
go goroutine(c)
c <- i
time.Sleep(10 * time.Second)
}
func goroutine(c chan int) {
for {
num := <- c
fmt.Println(num)
num++
time.Sleep(1 * time.Second)
c <- num
}
}
What I'm trying to do inside of goroutine is to receive number from channel, print it, increment and after one second send it back to the channel. After this I want to repeat the action.
But as a result, operation is only done once.
Output:
0
Am I doing something wrong?
By default the goroutine communication is synchronous and unbuffered: sends do not complete until there is a receiver to accept the value. There must be a receiver ready to receive data from the channel and then the sender can hand it over directly to the receiver.
So channel send/receive operations block until the other side is ready:
1. A send operation on a channel blocks until a receiver is available for the same channel: if there’s no recipient for the value on ch, no other value can be put in the channel. And the other way around: no new value can be sent in ch when the channel is not empty! So the send operation will wait until ch becomes available again.
2. A receive operation for a channel blocks until a sender is available for the same channel: if there is no value in the channel, the receiver blocks.
This is illustrated in the below example:
package main
import "fmt"
func main() {
ch1 := make(chan int)
go pump(ch1) // pump hangs
fmt.Println(<-ch1) // prints only 0
}
func pump(ch chan int) {
for i:= 0; ; i++ {
ch <- i
}
}
Because there is no receiver the goroutine hangs and print only the first number.
To workaround this we need to define a new goroutine which reads from the channel in an infinite loop.
func receive(ch chan int) {
for {
fmt.Println(<- ch)
}
}
Then in main():
func main() {
ch := make(chan int)
go pump(ch)
go receive(ch)
}
Go Playground
You create an unbuffered channel c with
c := make(chan int)
On unbuffered channels, operations are symmentrical, i.e. every send on a channel needs exactly one receive, every receive needs exactly one send. You send your i into the channel, the goroutine receives it into num. After that, the goroutine sends the incremented num into the channel, but nobody is there to receive it.
In short: The statement
c <- num
will block.
You could use a 1-buffered channel, that should work.
There is another problem with your code that you solved by waiting ten seconds in main: You don't know when your goroutine finishes. Typically, a sync.WaitGroup is used in these situations. But: Your goroutine does not finish. It's idiomatic to introduce a chan struct{} that you close in your main goroutine after a while and select over both channels in the worker goroutine.
You use unbuffered channel, so your goroutine hang on c <- num
You should use buffered channel, like this: c := make(chan int, 1)
Try it on the Go playground
I am seeing an inconsistency in the way unbuffered channels appear to work - this is either an inconsistency in Go, or in my understanding of Go...
Here is a simple example with output. The 'inconsistency' is with the 'make channel' lines.
package main
import (
"fmt"
)
func send(sendto chan string) {
fmt.Println("send 1")
sendto <- "Hello"
fmt.Println("send 2")
sendto <- "World"
fmt.Println("send 3")
sendto <- ""
fmt.Println("send() exit")
}
func main() {
//hole := make(chan string)
//hole := make(chan string, 0)
hole := make(chan string, 1)
go send(hole)
fmt.Println("main loop")
carryon := true
for carryon {
msg := <- hole
if msg == "" {
carryon = false
} else {
fmt.Println(" recd ", msg)
}
}
}
When I run as above, the output is as expected (and also as expected for a buffer size of 2). i.e. the channel has a buffer of 1, which holds one value - on next attempting to write, there is a context switch to the main to allow it to consume the first value.
main loop
send 1
send 2
recd Hello
send 3
recd World
send() exit
When I then change the make channel line to:
hole := make(chan string, 0)
The output is:
main loop
send 1
send 2
recd Hello
recd World
send 3
send() exit
I would have expected the send 2 and the recd Hello to be the other way around...
I get the same output for hole := make(chan string)
I checked the specification and it says
The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives).
Please can someone explain either
Why my expectations are wrong - please be kind
or whether Go is actually wrong
Thank you
Roughly: Send and receive happen concurrently. The details are explained in the Go Memory Model which determines this behaviour. Concurrent code is complicated...
This timeline for the two goroutines shows what's going on:
send() main()
fmt.Println("send 1")
sendto <- "Hello" msg := <- hole // sender and receiver both ready
fmt.Println("send 2")
fmt.Println(" recd ", msg) // msg is "Hello"
sendto <- "World" msg := <- hole // sender and receiver both ready
fmt.Println(" recd ", msg) // msg is "World"
fmt.Println("send 3")
sendto <- ""
fmt.Println("send() exit")
send 2 is printed before recd Hello because send() runs to the print statement before the runtime schedules main() to run again.
There is no happens before relationship for printing the two messages. They can be printed in either order.
communication succeeds only when both a sender and receiver are ready
The key is that this does not require the receiver to immediately start processing the message it has received. Specifically in your case, it is ready, so it receives the value without invoking the scheduler (no context switch). The goroutine continues running until it tries to send again, at which point the receiver is not ready, so the scheduler is invoked etc.