一尘不染

去教程选择语句

go

我正在浏览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)
}

我了解通道工作的基础知识,但是我没有得到的是上述select语句的工作方式。教程中的说明说:


select语句使goroutine等待多个通信操作。一个select阻塞直到它的一种情况可以运行,然后它执行该情况。如果有多个就绪,它将随机选择一个。”

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

案例:发送x到频道c

案例:收到戒烟

我想我知道第二个命令只有在quit有一个值时才执行,稍后在go func()内部完成。但是第一种情况要检查什么?另外,在go
func()内部,显然是从c打印值,但是c在那时候不应该包含任何内容?我能想到的唯一解释是go
func()在调用fibonacci()之后以某种方式执行。我猜这是一个我也不完全了解的goroutine,就像魔术一样。

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


阅读 242

收藏
2020-07-02

共1个答案

一尘不染

请记住,通道会阻塞,因此select语句为:

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.

这里的关键是要记住通道阻塞,并且您正在使用两个未缓冲的通道。利用go分拆的第二功能让您从消费c那么fibonacci将继续。


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

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

2020-07-02