I'm sure that there is a simple explanation to this trivial situation, but I'm new to the go
concurrency model.
when I run this example
package main import "fmt" func main() { c := make(chan int) c <- 1 fmt.Println(<-c) }
I get this error :
fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() /home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52 exit status 2
Why ?
Wrapping c <-
in a goroutine
makes the example run as we expected
package main import "fmt" func main() { c := make(chan int) go func(){ c <- 1 }() fmt.Println(<-c) }
Again, why ?
Please, I need deep explanation , not just how to eliminate the deadlock and fix the code.
An unbuffered channel is used to perform synchronous communication between goroutines while a buffered channel is used for perform asynchronous communication. An unbuffered channel provides a guarantee that an exchange between two goroutines is performed at the instant the send and receive take place.
Goroutines and Channels are a lightweight built-in feature for managing concurrency and communication between several functions executing at the same time.
We can close a channel in Golang with the help of the close() function. Once a channel is closed, we can't send data to it, though we can still read data from it. A closed channel denotes a case where we want to show that the work has been done on this channel, and there's no need for it to be open.
A goroutine is a function that executes simultaneously with other goroutines in a program and are lightweight threads managed by Go. A goroutine takes about 2kB of stack space to initialize.
From the documentation :
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.
Said otherwise :
This line
c <- 1
blocks because the channel is unbuffered. As there's no other goroutine to receive the value, the situation can't resolve, this is a deadlock.
You can make it not blocking by changing the channel creation to
c := make(chan int, 1)
so that there's room for one item in the channel before it blocks.
But that's not what concurrency is about. Normally, you wouldn't use a channel without other goroutines to handle what you put inside. You could define a receiving goroutine like this :
func main() { c := make(chan int) go func() { fmt.Println("received:", <-c) }() c <- 1 }
Demonstration
In unbuffered channel writing to channel will not happen until there must be some receiver which is waiting to receive the data, which means in the below example
func main(){ ch := make(chan int) ch <- 10 /* Main routine is Blocked, because there is no routine to receive the value */ <- ch }
Now In case where we have other go routine, the same principle applies
func main(){ ch :=make(chan int) go task(ch) ch <-10 } func task(ch chan int){ <- ch }
This will work because task routine is waiting for the data to be consumed before writes happen to unbuffered channel.
To make it more clear, lets swap the order of second and third statements in main function.
func main(){ ch := make(chan int) ch <- 10 /*Blocked: No routine is waiting for the data to be consumed from the channel */ go task(ch) }
This will leads to Deadlock
So in short, writes to unbuffered channel happens only when there is some routine waiting to read from channel, else the write operation is blocked forever and leads to deadlock.
NOTE: The same concept applies to buffered channel, but Sender is not blocked until the buffer is full, which means receiver is not necessarily to be synchronized with every write operation.
So if we have buffered channel of size 1, then your above mentioned code will work
func main(){ ch := make(chan int, 1) /*channel of size 1 */ ch <-10 /* Not blocked: can put the value in channel buffer */ <- ch }
But if we write more values to above example, then deadlock will happen
func main(){ ch := make(chan int, 1) /*channel Buffer size 1 */ ch <- 10 ch <- 20 /*Blocked: Because Buffer size is already full and no one is waiting to recieve the Data from channel */ <- ch <- ch }
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