I am stuck in a strange situation where the write operation to the channel never happens.
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int)
s := make(chan bool)
k := make(chan bool)
fmt.Println("start")
go func() {
fmt.Println("routine 1")
s <- true
}()
go func() {
fmt.Println("routine 2")
for {
select {
case <-s :
for i := 0; i < 3; i++ {
fmt.Println("before")
c <- i
fmt.Println("after")
}
case <- k :
fmt.Println("k ready")
break
}
}
}()
go func() {
fmt.Println("routine 3")
for {
select {
case x := <- c :
fmt.Println("x=", x)
k <- true
fmt.Println("k done")
}
}
}()
time.Sleep(1000 * time.Millisecond)
}
And here is the output:
start
routine 1
routine 2
before
routine 3
x= 0
after
before
I wonder why the write to the channel k blocks, but the log statement fmt.Println("k ready") is never printed.
Here is what I think :
go routine 1 writes true to channel s
go routine 2 writes 0 to
channel c and waits because buffer size is 0, it will not be able to
write '1' to it unless someone reads channel c
go routine 3 comes into the picture, reads channel c (now go routine 2
can write to c once go routine 2 resumes) prints the value of x. NOW IT SHOULD BE ABLE TO WRITE TO CHANNEL K but that is not happening
According to me it should be able to write to channel k then case 2 of goroutine should execute and print "k ready"
Can anyone explain me why write to the channel blocked?
As a fix I know I can increase the buffer size of channel c and everything will get printed but I am not interested in fixing this, instead I want to understand this scenario.
A nice blog to understand the above case.
You have a deadlock.
goroutine 1 writes to s then quits
goroutine 2 reads from s, and writes to c
goroutine 3 reads from c, and writes to k, and this blocks because nothing is reading from k, because goroutine 2 is blocked in the write to k above.
goroutine 2 writes to c again which blocks as goroutine 3 is still trying to write to k and thus is not reading from c
Contrary to what you say, you don't have a buffer size of 1. You have a buffer size of zero (i.e. an unbuffered channel), so a write will block until something reads. This is probably the source of your misunderstanding. Per the language specification:
A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:
make(chan int, 100)
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). A nil channel is never ready for communication.
Related
If I have multiple goroutines reading and writing to the same channel, are there any ways to close the channel at certain point, for example after a fix amount of elements are written.
func foo(c chan int) {
for i := range c {
// how to close the channel after 100 integers are written?
c <- i + 1
c <- i + 2
}
}
func bar() {
c := make(chan int, 200)
c <- 0
go foo(c)
go foo(c)
go foo(c)
}
Give conditional case would work for it:
for i := 0; i <= cap(c); i++ {
// how to close the channel after 100 integers are written?
c <- i + 1
// since you send 2 values to channel for each i loop, you need to half of it.
if i == 49 { //if you asking why 49, you have sent one value on bar function. Also to make it perfectly 100 integer in total.
close(ch)
break // terminating the function after the closing channel, it will run in panic because the function still running and keep sending value on channel which was already closed.
}
c <- i + 2
}
Output :
0
1
2
2
3
3
...
50
50
Just give some information about channel, don't use range on channel if you havent sent anything on it...
If you put "i range channel" before you have value on channel, it will not read anything.
Back to again to your code
If you run this code :
func bar() {
c := make(chan int, 200)
c <- 0
go foo(c)
go foo(c) // run in panic
go foo(c) // run in panic
}
It will run in panic, since the channel already closed which mean your channel not accepting any values again.
Take a look at this picture, let say you make a buffered channel with 3 capacity. And you going to close after receiving 2 values to send it will run in panic if you try to send values on it again.
I read the book "Concurrency in Go" written by Katherine Cox-Buday and I don't understand comments for examples of buffered channels.
The author says:
if a goroutine making writes to a channel has knowledge of how many writes it will make,
it can be useful to create a buffered channel whose capacity is the number of writes to be made
It sounds clear, but examples are confusing.
First example - source: https://github.com/kat-co/concurrency-in-go-src/blob/master/gos-concurrency-building-blocks/channels/fig-using-buffered-chans.go#L13
var stdoutBuff bytes.Buffer // <1>
defer stdoutBuff.WriteTo(os.Stdout) // <2>
intStream := make(chan int, 4) // <3>
go func() {
defer close(intStream)
defer fmt.Fprintln(&stdoutBuff, "Producer Done.")
for i := 0; i < 5; i++ {
fmt.Fprintf(&stdoutBuff, "Sending: %d\n", i)
intStream <- i
}
}()
for integer := range intStream {
fmt.Fprintf(&stdoutBuff, "Received %v.\n", integer)
}
The line with comment <3> has the following explanation:
Here we create a buffered channel with a capacity of one.
There is 4, not 1. Is it a mistake?
Second example - channel ownership, source: https://github.com/kat-co/concurrency-in-go-src/blob/master/gos-concurrency-building-blocks/channels/fig-chan-ownership.go
chanOwner := func() <-chan int {
resultStream := make(chan int, 5) // <1>
go func() { // <2>
defer close(resultStream) // <3>
for i := 0; i <= 5; i++ {
resultStream <- i
}
}()
return resultStream // <4>
}
The line marked as <1>, has the following comment:
Since we know we will produce six results, we create a buffered channel of five
so that the goroutine can complete as quickly as possible.
I completely don't understand this comment. Goroutine will be blocked, because the channel has the capacity of 5 messages and there will be produced 6 messages, so it will wait until a receiver takes the first message.
Since we know we will produce six results, we create a buffered channel of five so that the goroutine can complete as quickly as possible.
You are correct that the goroutine will block until a value is received.
It doesn't make sense to create a channel with capacity one less than the number of values to be sent. The goroutine can be eliminated if the channel capacity is equal to or greater than the number of values:
chanOwner := func() <-chan int {
resultStream := make(chan int, 6)
for i := 0; i < cap(resultStream); i++ {
resultStream <- i
}
close(resultStream)
return resultStream
}()
or this by eliminating the anonymous function:
chanOwner := make(chan int, 6)
for i := 0; i < cap(chanOwner); i++ {
resultStream <- i
}
close(chanOwner)
Yes, it sounds like this book needs a better editor!
the channel capacity is indeed indicated as the 2nd argument to make:
intStream := make(chan int, 4) // buffered-channel of capacity 4 (not 1!)
If no reads are done on the channel - then yes the writing goroutine will write 5 times to the buffered channel (of capacity 5) without issue (i.e. without blocking). The 6th write will indeed block - and the goroutine will not return until the buffer congestion reduces.
If some other goroutine does read from the channel - even just once - then the buffer frees up and the writer goroutine will be able to complete the final write.
There is 4, not 1. Is it a mistake?
It seems a typo. As clearly stated in the documentation, the second argument to make is the channel capacity:
Channel: The channel's buffer is initialized with the specified
buffer capacity. If zero, or the size is omitted, the channel is unbuffered.
Therefore make(chan int, 4) is a chan with capacity 4.
Goroutine will be blocked, because the channel has the capacity of 5 messages and there will be produced 6 messages, so it will wait until a receiver takes the first message.
Correct, the declared chan has capacity 5, and if there's no receiver, the sixth and last send operation will indeed block, as the channel buffer is full.
Assuming good faith on all sides, the book probably missed a round of proofreading.
I just started learning golang, while I was going through concurrency I accidentally wrote this code:
import (
"fmt"
)
func squares(c chan int) {
for i := 0; i < 4; i++ {
num := <- c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main start")
c := make(chan int)
go squares(c)
c <- 1
c <- 2
c <- 3
c <- 4
go squares(c)
c <- 5
c <- 6
c <- 7
c <- 8
fmt.Println("main stop")
}
Originally I was suppose to assign c := make(chan int, 3).
I am having trouble understanding the output of the code I've written.
main start
1
4
9
25
36
49
16
main stop
I would like to understand how the code is executed.
I was expecting error: all goroutines are asleep - deadlock!
Many thanks!
I am not sure to really understand what you wanted to achieve, especially the reason of that weird loop :
for i := 0; i < 4; i++ {
num := <- c
fmt.Println(num * num)
}
But anyway.
First have all, some explanations on how works channels and goroutines.
A Channel is a thread safe messaging pipe used to share data accross differents executions contexts. A channel is creating with make instruction, then
c := make(chan int, 3)
means create a channel of int type with a "buffer" size of 3. This element is very important to understand. Channel do follow a producer / consumer pattern with that bases rules :
For producer :
if I try to push some data in a channel with some "free space", it doesn't block and next instructions are executed
If I try to push some data in a channel without "free space", it blocks till previous has been treated
For consumer :
If I try to take element from an empty channel, it block untill some data come
If I try to take element from a non empty channel, it take the first (Channel is a FIFO)
Please note that all blocking operations may be turned non-blocking using some special pattern available through select instruction, but that's another subject :).
Goroutine are light sub-processes, routines (No thread). It is no place here to explain all in details, but what is important is 1 goroutine === 1 execution stack.
So in your case, the output is quite predictable till there is only one consumer. Once you start the second Goroutine, the consumption order is still predictable (the size of the channel is only one), but the execution order not !
One thing is noticable: you have only 7 output... That because your main goroutine ends before it happends, terminating the whole program execution. That point explain why you didn't have the all goroutines are asleep - deadlock!.
If you want to have that, you just should add <- c somewhere at the end of the main :
package main
import (
"fmt"
)
func squares(c chan int) {
for i := 0; i < 4; i++ {
num := <-c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main start")
c := make(chan int)
go squares(c)
c <- 1
c <- 2
c <- 3
c <- 4
go squares(c)
c <- 5
c <- 6
c <- 7
c <- 8
<-c
fmt.Println("main stop")
}
You will have the behavior I think you expect :
main start
1
4
9
25
36
49
64
16
fatal error: all goroutines are asleep - deadlock!
Edit : on step by step, execution :
// a goroutine is created and c is empty. because
// the code of `squares` act as a consumer, the goroutine
// "block" at instruction `num := <-c`, but not the main goroutine
go squares(c)
// 1 is pushed to the channel. Till "c" is not full the main goroutine
// doesn't block but the other goroutine may be "released"
c <- 1
// if goroutine of squares has not consume 1 yet, main goroutine block
// untill so, but is released just after
c <- 2
// it continues with same logic
c <- 3
c <- 4
// till main goroutine encountered `<- c` (instruction I added) .
// Here, it behave as a consumer of "c". At this point all
// goroutine are waiting as consuler on "c" => deadlock
The following code keeps printing 0.
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
}
From my understanding, it should print 0 to 9 and then go on an infinite loop. Why does it keep printing zero?
Your understanding is (almost) correct, except that the infinite loop in the main goroutine will not block but will receive and print relentlessly.
The goroutine you start sends the numbers 0, 1, ... 9 on the channel, then closes it. And receiving from a closed channel does not block, on the contrary, it can proceed immediately, and it yields the zero value of the element type of the channel, which is 0 for the type int. This is stated in Spec: Receive operator:
Receiving from a nil channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
So you see exactly what you should. First it prints the numbers 0..9, then keeps printing 0 very fast (without any delay), so probably you don't even notice the initial 0..9 numbers.
Slightly modifying your example so that the loop in the main goroutine exits after 15 iterations will immediately show what happens:
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for i := 0; i < 15; i++ {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
Output (try it on the Go Playground):
012345678900000
If your goal is to quit once all sent numbers have been processed (printed), use the for range on the channel:
for i := range c {
fmt.Print(i) // RECEIVER; OUTPUT
}
If your goal is to block until new numbers are available once those 10 numbers are processed, then don't close the channel. That way the next receive operation will block. In this case both your original loop and the for range loop would work, but for range is better as that always exits if / when the channel is closed.
Check out this question and memorize the channel axioms: How does a non initialized channel behave?
I am learning how channels work in Go and have stumbled upon a problem with closing the channels. This is a modified example from A Tour of Go, which generates n-1 fibonacci numbers and sends them through the channels, leaving the last "element" of the channel capacity unused.
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n-1; i++ {
c <- x
x, y = y, x+y
}
// close(c) // It's commented out on purpose
}
func main() {
n := 10
c := make(chan int, n)
go fibonacci(n, c)
for i := 0; i < n; i++ {
_, ok := <-c
fmt.Println(ok)
}
}
The problem is that I get:
fatal error: all goroutines are asleep - deadlock!
when I do not close the channel. What exactly is causing the deadlock? Why can't I receive from the channel in its capacity boundaries when I don't close it?
You're writing n values into the channel (from 0 to n-1), but are trying to read n+1 values from the channel (from 0 to n). Without explicitly closing the channel, the main function will wait forever for this last value.
What exactly is causing the deadlock?
After n iterations, the goroutine running the fibonacci function will exit. After this goroutine has exited, the only remaining goroutine in your program is the main goroutine, and this goroutine is waiting for some data to be written to the c channel -- and since there is no other goroutine left that might ever write data into this channel, it will wait forever. This is exactly what the error message is trying to tell you: "all goroutines ("all" is just "one", here) are asleep".
The _, ok := <- c call in the main function will only stop blocking as soon as the c channel is closed (as reading from a channel is blocking, this needs to be done from another goroutine). When the channel is closed, the main function will read remaining data from the channel (when it's a buffered channel)
For loop in main expect n communication in channel, but you produce only n-1 in func fibonacci
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ { //here
c <- x
x, y = y, x+y
}
// close(c) // It's commented out on purpose
}
should work
http://play.golang.org/p/zdRuy14f9x