Here is a simple example code about unbuffered channels:
ch01 := make(chan string)
go func() {
fmt.Println("We are in the sub goroutine")
fmt.Println(<-ch01)
}()
fmt.Println("We are in the main goroutine")
ch01 <- "Hello"
The result I got:
We are in the main goroutine
We are in the sub goroutine
Hello
Go playground: https://play.golang.org/p/rFWQbwXRzGw
From my understanding, the send operation blocked the main goroutine, until the sub goroutine executed a receive operation on channel ch01
. Then the program exited.
After placing the sub goroutine after the send operation like that:
fmt.Println("We are in the main goroutine")
ch01 <- "Hello"
go func() {
fmt.Println("We are in the sub goroutine")
fmt.Println(<-ch01)
}()
A deadlock occurred:
We are in the main goroutine
fatal error: all goroutines are asleep - deadlock!
go playground https://play.golang.org/p/DmRUiBG4UmZ
What happened this time? Did that mean after ch01 <- "Hello"
the main goroutine was immediately blocked so that the sub goroutine had no chance to run? If it is true, how should I understand the result of the first code example?(At first in main goroutine, then in sub goroutine).
An unbuffered channel blocks on send until a receiver is ready to read. In your first example a reader is set up first, so when the send occurs it can be sent immediately.
In your second example, the send happens before a receiver is ready so the send blocks and the program deadlocks.
You could fix the second example by making a buffered channel, but there is a chance you won't ever see the output from the goroutine as the program may exit (the main goroutine) before the output buffer is flushed. The goroutine may not even run as main exits before it can be scheduled.
First of all, go-routines
run concurrently. In 1st example, the sub-goroutine has already started, but in 2nd example, the go-routine hasn't started yet when the send operation appears.
Think about line by line.
In 1st example, the sub-goroutine
has started concurrently before the send operation appears on the main go-routine. As a result, when the the send operation happens, there is already an receiver (sub-goroutine
) exists.
If you tweak the 1st example,
package main
import (
"fmt"
"time"
)
func main() {
ch01 := make(chan string)
go func() {
fmt.Println("We are in the sub goroutine")
fmt.Println(<-ch01)
}()
// wait for start of sub-routine
time.Sleep(time.Second * 2)
fmt.Println("We are in the main goroutine")
ch01 <- "Hello"
// wait for the routine to receive and print the string
time.Sleep(time.Second * 2)
}
The output will be
We are in the sub goroutine
We are in the main goroutine
Hello
So, you can see that the sub-goroutine has already started.and it is waiting to receive on channel. When the main goroutine send string in channel, the sub-goroutine resumes and receives the signal.
But in 2nd example, The program has stuck in main go routine send operation
, and the sub go routine has not started yet and will not start, because the program has not got that line yet. so there is no other receiver to receive the signal. So the program stuck in deadlock.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With