Honord
Honord

Reputation: 111

Need help understand logic behind concocurrent of goroutine ,select and channel

I try to understand logic behind concurrency of goroutine ,select and channel. The sample code is below. The base code is from tour go. I add some Printf to help me understand better.

package main
import "fmt"
func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            fmt.Printf("(%v, %v)\n", x ,y)
            x, y = y, x+y
            fmt.Printf("(%v, %v)\n", x ,y)
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}
func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 4; i++ {
            fmt.Println(<-c)
            fmt.Printf("%v from main\n",i)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

Output is

0
0 from main
(0, 1)
(1, 1)
(1, 1)
(1, 2)
1
1 from main
1
2 from main
(1, 2)
(2, 3)
(2, 3)
(3, 5)
2
3 from main
quit

There are concurrency behind goroutine and channel operation. My question is why the output is not

0
0 from main
(0, 1)
(1, 1)
1
1 from main
(1, 1)
(1, 2)
1
2 from main
(1, 2)
(2, 3)
2
3 from main
(2, 3)
(3, 5)
quit

Upvotes: 0

Views: 41

Answers (1)

David Maze
David Maze

Reputation: 158822

It might clarify things to rewrite the body of the for loop in the goroutine slightly:

x := <-c
// block until a value has been read, then continue
fmt.Println(x)
fmt.Printf("%v from main\n",i)

And similarly the body of the select loop in fibonacci() is effectively:

c <- x
// block until a value has been written, then continue
fmt.Printf("(%v, %v)\n", x ,y)
x, y = y, x+y
fmt.Printf("(%v, %v)\n", x ,y)

In both cases you have a guarantee that the second print statement will print after the first.

When you run the program, say the goroutine starts immediately. It gets up to this read, but there is no value in the channel, so it blocks. Then the main program calls fibonacci(). It gets to the select statement. There is a reader for the channel c, so it sends the number x there.

Once this has happened both the goroutine and the main program are free to go. The select statement has triggered one of its branches and sent its value; the read has completed. Both goroutines are free to run, and can do so in any order (so long as each executes its own statements in order). You'll eventually reach a point where either the goroutine is blocked on the read or fibonacci() is blocked on the select, and once both catch up to each other, both will be free to execute again.

The order you propose would require the reader to "wake up" before the writer did, but nothing in Go requires this, and indeed on a multi-core system both can be running at the same time.

Upvotes: 1

Related Questions