这个 go 并发示例中发生了什么?

What is happening in this go concurrency example?

我这里是并发示例: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)
}

关于这件事有很多让我困惑的地方,旅游本身并没有说太多。它说 select 将等待通信操作,但我真的不明白它如何应用于它在调用 fibonacci 之前发送到 goroutine 的函数。 fmt.Println 正在通过频道 c 发送,但我不明白为什么或这有什么意义...

我对此有很多疑问,我希望在理解第一部分后一切都落到实处...

在代码示例中,goroutine 启动并运行,直到它到达 fmt.Println(<- c),它正在打印从 c 接收到的值。此时 i == 0 我们正处于第一次迭代。同时,由于这是 goroutine 中的 运行,main 继续执行并调用 fibonacci 将其传递给 cquit 通道。 fibonacci 函数位于 'infinite' select 中,这意味着如果不显式调用 return 就永远不会退出此函数,就像它从 [=15= 接收时所做的那样]频道。

所以回到 main,在你调用 goroutine 的地方,它会阻塞直到从通道接收,fibonacci 开始在那个通道上执行和发送,提供 fibonacci 序列中的下一个数字直到该循环终止(当 i == 10 时),此时那里的代码向下移动到 quit <- 0 在退出通道上发送的位置。由于您在 fibonacci 那里处于无限 select 中,它将始终执行下一个可用案例,它无法继续在 c 上发送,因为该通道没有缓冲,它只允许一个item 并且已满,而是在 quit case 语句中移动,因为它在 quit 通道上收到了一些东西,此时它打印 quit 和 returns.

希望对您有所帮助。有些语言可能不是 100% 准确。真的只是想说明这个控制流是如何工作的。我认为实际上, select 一直等到 main 中的 goroutine 可以接收,此时它进入 c <- x 语句并发送。您可以通过在完成所有操作后尝试从通道中读取一个项目来观察这一点,这会导致恐慌。实际上,它们都在等待彼此,你不能在一个完整的频道上发送,如果你只是在没有任何其他控制流的情况下读取它(比如 select),那么你将阻塞直到那里有东西接收。