转到教程选择语句

标签 go switch-statement channel

我正在研究 tour.golang.org 上的示例,我遇到了这段我不太理解的代码:

package main
import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x: // case: send x to channel c?
            x, y = y, x+y
        case <-quit: // case: receive from channel quit?
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() { // when does this get called?
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

我了解 channel 工作原理的基础知识,但我不明白上面的 select 语句是如何工作的。教程的解释是:

“select 语句让 goroutine 等待多个通信操作。 select 阻塞直到它的一个 case 可以运行,然后它执行那个 case。如果多个准备就绪,它会随机选择一个。”

但是这些案件是如何执行的?据我所知,他们说:

案例:将x发送到 channel c

案例:从退出接收

我想我明白第二个只有当 quit 有一个值时才会执行,这是稍后在 go func() 中完成的。但是第一个案例检查的是什么?此外,在 go func() 内部,我们显然是从 c 中打印值,但此时 c 中不应该有任何内容吗?我能想到的唯一解释是 go func() 以某种方式在调用 fibonacci() 之后执行。我猜这是一个我也不完全理解的 goroutine,它看起来很神奇。

如果有人可以检查这段代码并告诉我它在做什么,我将不胜感激。

最佳答案

请记住 channel 会阻塞,因此选择语句如下:

select {
case c <- x: // if I can send to c
    // update my variables
    x, y = y, x+y
case <-quit: // If I can receive from quit then I'm supposed to exit
    fmt.Println("quit")
    return
}

没有 default 的情况意味着“如果我不能发送到 c 并且我不能从 quit 中读取,阻塞直到我可以。”

然后在你的主进程中你分离出另一个从c读取的函数来打印结果

for i:=0; i<10; i++ {
    fmt.Println(<-c)  // read in from c
}
quit <- 0  // send to quit to kill the main process.

这里的关键是要记住 channel 阻塞,并且您正在使用两个无缓冲 channel 。使用 go 派生出第二个函数让您可以从 c 消费,所以 fibonacci 将继续。


协程是所谓的“绿色线程”。使用关键字 go 开始函数调用会将其分离到一个独立于主执行线运行的新进程中。本质上,main()go func() ... 是同时运行的!这很重要,因为我们在此代码中使用了生产者/消费者模式。

fibonacci 生成值并将它们发送到 c,而从 main 生成的匿名 goroutine 使用来自 c 的值并处理它们(在在这种情况下,“处理它们”只是意味着打印到屏幕上)。我们不能简单地生成所有值然后使用它们,因为 c 会阻塞。此外,fibonacci 将永远产生更多的值(或者直到整数溢出为止),因此即使您有一个具有无限长缓冲区的魔法 channel ,它也永远不会到达消费者。

关于转到教程选择语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34931059/

相关文章:

go - 为什么我的 golang 程序创建了这么多线程?

php - 是否可以在 switch 中使用 for 循环?

dictionary - 在 Golang 中将命名类型映射[字符串]字符串转换为普通类型

Golang 如何从矩形 jpeg 裁剪圆形图像。

javascript - 使用 switch 语句选择单选按钮时更改 href 值

c - 切换大小写表达式

objective-c - 如何使用 objective-c 实现 go 样式 channel (CSP)?

go - 是否可以在 Go 中同时等待 channel 和文件描述符?

go - 停止阻塞的 goroutine

go - gobwas/ws通过网络发送操作