需要帮助理解goroutine、select和channel的当前版本背后的逻辑吗

需要帮助理解goroutine、select和channel的当前版本背后的逻辑吗,go,Go,我试图理解goroutine、select和channel并发背后的逻辑。下面是示例代码。基本代码来自tour go。我添加了一些Printf来帮助我更好地理解 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 ,

我试图理解goroutine、select和channel并发背后的逻辑。下面是示例代码。基本代码来自tour go。我添加了一些Printf来帮助我更好地理解

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)
}
goroutine和channel操作背后存在并发性。我的问题是,为什么输出不正确

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

在goroutine中稍微重写for循环的主体可能会澄清一些问题:

x := <-c
// block until a value has been read, then continue
fmt.Println(x)
fmt.Printf("%v from main\n",i)
在这两种情况下,您都可以保证第二个print语句将在第一个print语句之后打印

当你运行程序时,假设goroutine立即启动。它将进行此读取,但通道中没有值,因此会阻塞。然后主程序调用斐波那契。它到达select语句。通道c有一个读卡器,所以它将数字x发送到那里

一旦发生这种情况,goroutine和主程序都可以自由运行。select语句已触发其一个分支并发送其值;阅读已完成。两个goroutine都可以自由运行,并且可以按任意顺序运行,只要每个goroutine都按顺序执行自己的语句。您最终将到达这样一个点,即goroutine在read上被阻塞,或者fibonacci在select上被阻塞,一旦两者相互赶上,就可以再次自由执行

您提出的顺序要求读者在作者之前醒来,但Go中没有任何要求,事实上,在多核系统上,两者可以同时运行

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