I'm new to the Go language and am currently going through the Go tour. I have a question regarding the concurrency example 5 on the select statement.
The code below has been edited with print statements to trace statements execution.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
fmt.Printf("Run fib with c: %v, quit: %v\n", c, quit)
for {
select {
case c <- x:
fmt.Println("Run case: c<-x")
x, y = y, x+y
fmt.Printf("x: %v, y: %v\n", x, y)
case <-quit:
fmt.Println("Run case: quit")
fmt.Println("quit")
return
}
}
}
func runForLoop(c, quit chan int) {
fmt.Println("Run runForLoop()")
for i := 0; i < 10; i++ {
fmt.Printf("For loop with i: %v\n", i)
fmt.Printf("Returned from c: %v\n", <-c)
}
quit <- 0
}
func main() {
c := make(chan int)
quit := make(chan int)
go runForLoop(c, quit)
fibonacci(c, quit)
}
The following is printed to the console.
Run fib with c: 0xc00005e060, quit: 0xc00005e0c0
Run runForLoop()
For loop with i: 0
Returned from c: 0 // question 1
For loop with i: 1
Run case: c<-x // question 2
x: 1, y: 1
Run case: c<-x // question 2
x: 1, y: 2
Returned from c: 1
For loop with i: 2
Returned from c: 1
For loop with i: 3
// ...
My questions are
The value of c is received here is 0 even though none of the select blocks have been executed. Could I confirm that this is the zero value of the c variable that has int type?
Why is case c<-x executed twice?
For 1: It prints the result of <-c, which will block until another goroutine writes to it. So your statement is not correct: the select case for c<-x ran, with x=0. It is not the zero value of the chan variable. You will only read the zero-value of the chan type from a channel if the channel is closed, or if you use the two-value form of channel read: value,ok := <-c. When ok=false, value is the zero-value of the channel value type.
For 2: c<-x will execute 10 times, because you read from it 10 times in the for-loop, and only then you are writing to quit, which will enable the second case of the select. What you observe here is the second iteration of the loop.
Related
Here is the code from A tour of Go to demonstrate the use of select statements in Go.
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
return
}
}
}
My question is :
what is case c <-x doing here? is some data x being sent to the channel or we are looking for the condition when some data is being sent to the channel.
If some data is being sent to the loop, the loop should never end and if the condition of a data sent to channel is evaluated I cannot see any data being sent to the channel.
The main function is:
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
That case c <- x: will be successful if channel c is free to receive data only. And it successfully send that value of x, then x's value is changed to y's previous value with line x, y = y, x+y and populate fibonacci.
In this way, If there is no receiver to the channel c, there is no blocking because the loop always continuing and quit when the quit channel receives a value.
In the main function, inside the goroutine channel receives messages sent from the select case and free up the channel for a new message.
Please refer to this example for more understanding.
There is no error when I run the following code, but nothings gets printed either:
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
counter := 0
for {
select {
case c <- x:
fmt.Println("sent x", x)
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
counter += 1
fmt.Println("Counter", (counter));
}
fmt.Println("Fib exiting");
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println("got" , <-c)
}
quit <- 0
fmt.Println("Anon exiting");
}()
go fibonacci(c, quit)
}
I was thinking at least "Fib exiting" should be printed. If I execute fibonacci() normally in the main goroutine, it works.
Thanks in Advance...
Is it not guaranteed that all statements gets executed in a goroutine?
All statements that should execute according to your program control flow will execute.
Your fmt.Println("Fib exiting") print call doesn't execute because main() is returning immediately after spawning the two goroutines.
However even if main() were properly synchronized, fmt.Println("Fib exiting") is still unreachable code, because the select statement in fibonacci() either sends on c or receives on quit, and neither of those cases break out of the loop.
i have been following the go tour examples, and i don't understand how this works
https://tour.golang.org/concurrency/5
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
how is this working?
and while i'm trying to understand.
package main
import "fmt"
func b(c,quit chan int) {
c <-1
c <-2
c <-3
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
b(c,quit)
}
this will sometimes print 1,2 sometimes print 1,2,3, why?
First, in the func fibonacci, the select statement tries select the first thing of the two following to finish:
c <- x
<- quit
It is fairly easy to understand <- quit, which is tries to receive a value from a channel called quit (and ignores the value received).
c <- x means sending a value that equals (is a copy of) x. It seems like unblocking, but in Go, sending over an unbuffered channel (which is explained in the Go tour) blocks when there is no receiver.
So here it means, waiting for a receiver to be ready to receive the value (or a space in the buffer, if it were a buffered channel), which in this code means fmt.Println(<-c), and then send the value to the receiver.
So this statement unblocks (finishes) whenever <-c is evaluated. That is, every iteration of the loop.
And for your code, while all value 1, 2, 3, is guaranteed to be sent over the channel (and received), func b returns and thus func main retains without guaranteeing the fmt.Println(3) finishes.
In Go, when the func main returns, the program terminates, and unfinished goroutine does not get a chance to finish its work - thus sometimes it prints 3 and soetimes it does not.
To better understand the Fibonacci example let's analyze the different parts.
First the anonymous function
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
The "go" keyword will start a new goroutine, so now we have the "main" goroutine and this one, they're gonna be running concurrently.
The loop is telling us that we are going to execute this 10 times:
fmt.Println(<-c)
This call will block until we receive an integer from the channel and print it. Once this happens 10 times we are gonna signal that this goroutine has finished
quit <- 0
Now let's go back to the main goroutine, while the other goroutine was starting it has invoked the function "finbonacci", there's no "go" keyword so this call is blocking here.
fibonacci(c, quit)
Now let's analyze the function
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
We start an infinite loop and on each iteration, we will try to execute one case from the select
The first case will try to send Fibonacci's sequence values indefinitely to the channel, at some point, once the other goroutine reaches the fmt.Println(<-c) statement, this case will be executed, the values will be recalculated and the next iteration of the loop will happen.
case c <- x:
x, y = y, x+y
The second case won't be able to run yet, since nobody is sending anything to the "quit" channel.
case <-quit:
fmt.Println("quit")
return
}
After 10 iterations the first goroutine will stop receiving new values, thus the first select case won't be able to run
case c <- x: // this will block after 10 times, nobody is reading
x, y = y, x+y
At this point, no case in the "select" can be executed, the main goroutine is "blocked" (more like paused from a logical point of view).
Finally, at some point, the first goroutine will signal that it has finished using the "quit" channel
quit <- 0
This allows the execution of the second "select" case, which will break the infinite loop and allow the "fibonacci" function to return and the main function will be able to finish in a clean way.
Hopefully, this helps you understand the Fibonacci example.
Now, moving to your code, you are not waiting for the anonymous goroutine to finish, in fact, it's not even finishing. Your "b" method is sending "1,2,3" and returns immediately.
Since it's the last part of the main function, the program terminates.
If you only see "1,2" sometimes is because the "fmt.Println" statement is too slow and the program terminates before it can print the 3.
I'm studying Go lang through 'A tour of Go', and it's hard to understand Go channel running sequence,
package main
import "fmt"
import "time"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
time.Sleep(1000 * time.Millisecond)
sum += v
}
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
fmt.Println("Print this first,")
}
If run above the code, I expected,
Print this first,
17 -5 12
Because, Go routine runs as non-blocking, a But, actually it prints,
17 -5 12
Print this first,
The other example that I found in internet,
package main
import "fmt"
type Data struct {
i int
}
func func1(c chan *Data ) {
fmt.Println("Called")
for {
var t *Data;
t = <-c //receive
t.i += 10 //increment
c <- t //send it back
}
}
func main() {
c := make(chan *Data)
t := Data{10}
go func1(c)
println(t.i)
c <- &t //send a pointer to our t
i := <-c //receive the result
println(i.i)
println(t.i)
}
Also, I expected, it prints "Called" first, but the result is
10
20
20
Called
What I am misunderstanding? please help me to understand Go routine and channel.
In your first example, x, y := <-c, <-c will block until it reads off c twice, and then assign values to x, y. Channels aside, you have an assignment, a print statement, then another print statement. Those are all synchronous things, and will happen in the order you state them in. There's no way the second print statement would print first.
The second one is because fmt.Println writes to STDOUT and println to STDERR. If you are consistent (say use println everywhere) then you see:
10
Called
20
20
That's cause there's a race between the first println(t.i) in the main and the println("Called") that's happening in the goroutine. I'm guessing with GOMAXPROCS set to 1, this will happen consistently. With GOMAXPROCS set to NumCPU, I get a mix of results, sometimes looking like the above, and sometimes like this:
10Called
20
20
A short program about select statement for channels.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case s := <-quit:
fmt.Println("quit =",s)
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 9
}()
fibonacci(c, quit)
}
The result of the code above:
0
1
1
2
3
5
8
13
21
34
quit = 9
It worked fine. But after I changed (in func fibonacci)
case s := <-quit:
fmt.Println("quit =",s)
to
case <-quit:
fmt.Println(<-quit)
an fatal error occurred:
0
1
1
2
3
5
8
13
21
34
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.fibonacci(0x18348040, 0x18348080)
/tmp/compile42.go:12 +0xf9
main.main()
/tmp/compile42.go:27 +0x11c
Where does the error come from?
In second case you are getting the value form the channel two times. Every time you do something like <-channel you pop one value out of channel.
Hence program is waiting indefinitely on line
fmt.Println(<-quit)
but fortunately go is intelligent enough to detect this situation and panic with error "all goroutines are asleep - deadlock!"
The line
fmt.Println(<-quit)
is waiting for another value on the channel, which will never come, according to the code you have.
You have to keep in mind the line before in the select:
case s := <-quit
has already removed the quit value from the channel.
Thus it will never complete.