去教程选择语句

Tou*_*own 7 channel go switch-statement

我正在浏览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)
}
Run Code Online (Sandbox Code Playgroud)

我理解通道如何工作的基础知识,但我没有得到的是上面的select语句是如何工作的.该教程的解释说:

"select语句允许goroutine等待多个通信操作.一个选择块直到其中一个案例可以运行,然后执行该情况.如果多个就绪,它会随机选择一个."

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

case:将x发送到通道c

案例:从退出接收

我想我明白第二个只有当quit有一个值时执行,这在go func()中稍后会有.但是检查的第一个案例是什么?另外,在go func()中,我们显然是从c打印值,但c在那个时候不应该有任何东西?我能想到的唯一解释是go func()以某种方式在调用fibonacci()之后执行.我猜它是一个我不完全理解的goroutine,它看起来像是魔术.

如果有人能够通过这段代码告诉我它在做什么,我会很感激.

Ada*_*ith 9

请记住,通道将阻塞,因此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
}
Run Code Online (Sandbox Code Playgroud)

没有default案件意味着"如果我不能发送到c而且我无法从退出中读取,请阻止直到我可以."

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

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

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


Goroutines是所谓的"绿线".使用关键字启动函数调用将其go旋转到一个独立于主要执行线运行的新进程.从本质上讲,main()并且go func() ...同时运行!这很重要,因为我们在此代码中使用了生产者/消费者模式.

fibonacci生成值并将它们发送给c,并且从main生成的匿名goroutine从中消耗c并处理它们(在这种情况下,"处理它们"只意味着打印到屏幕上).我们不能简单地生成所有值然后消耗它们,因为c会阻塞.此外,fibonacci永远会产生更多的值(或者直到整数溢出),所以即使你有一个具有无限长缓冲区的魔术通道,它也永远不会传递给消费者.


Hie*_*han 7

理解此代码示例有两个关键点:

首先,让我们回顾一下无缓冲通道的工作原理。从文档中

如果通道未缓冲,则发送方会阻塞,直到接收方收到该值。

请注意,代码示例中的两个通道cquit都是无缓冲的。

其次,当我们使用go关键字启动一个新的goroutine时,执行将与其他例程并行发生。因此,在示例中,我们运行了两个 go 例程:由 启动的例程,以及由内部func main()启动的例程。go func()...func main()

我在这里添加了一些内联注释,这应该会让事情变得更清楚:package main import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for { // this is equivalent to a while loop, without a stop condition
        select {
        case c <- x: // when we can send to channel c, and because c is unbuffered, we can only send to channel c when someone tries to receive from it
            x, y = y, x+y
        case <-quit: // when we can receive from channel quit, and because quit is unbuffered, we can only receive from channel quit when someone tries to send to it
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() { // this runs in another goroutine, separate from the main goroutine
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit) // this doesn't start with the go keyword, so it will run on the go routine started by func main()
}
Run Code Online (Sandbox Code Playgroud)