我正在浏览tour的示例.golang .org,我遇到了这个我不太理解的代码:

package main
import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x: //  case :将x发送到通道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语句是如何工作的.教程上的说明是这样说的:

"The select statement lets a goroutine wait on multiple communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready."

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

case :将x发送到通道c

case :从退出接收

我想我理解了第二个函数只有在Quit有一个值的情况下才会执行,这个值稍后在go函数()中完成.但是第一个判断的 case 是什么呢?另外,在go func()中,我们显然是打印c中的值,但是c在这一点上不应该有任何内容?我能想到的唯一解释是go func()在调用Fibonacci()之后以某种方式执行.我猜这是一只猩猩,我也不完全理解,它看起来就像是魔术.

如果有人能通读这段代码并告诉我它在做什么,我将不胜感激.

推荐答案

请记住,频道将挡路,因此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个 case 意味着"如果我无法发送到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.

这里的关键是要记住频道挡路,并且您使用的是两个无缓冲的频道.使用go来关闭第二个函数允许您从c开始消费,因此fibonacci将继续.


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

fibonacci生成值并将其发送到c,而从main派生的匿名goroutine使用c中的值并对其进行处理(在本例中,"处理它们"只意味着打印到屏幕上).我们不能简单地创造所有的价值然后消费它们,因为c将会是挡路.此外,fibonacci将永远生成更多的值(或者直到整数溢出),因此即使您有一个具有无限长缓冲区的魔术通道,它也永远不会到达消费者手中.

Go相关问答推荐

错误.如果它包含切片,则返回FALSE

GO框架Echo中间件的使用

在整个SQL事务中将使用上下文作为默认设置吗?

理解Golang中的IOTA和常量

重新赋值变量时未清除动态类型-这是错误吗?

如何使用 Go 连接到非默认 firestore 数据库?

Kusto Go API 从多个表查询

用接口来模拟amqp091go的困难

GORM中是否可能自动迁移具有循环关系的表?

从单词中删除特殊字符

如何使用名称具有包名称的嵌套 struct 启动 go struct

Gorm 预加载给出了模糊的列错误

为什么在单独的 go routine 中需要 wg.Wait() 和 close() ?

自定义指标未显示在 prometheus web ui 中,grafana 中也是如此

将接口方法的参数限制为几个允许的 struct ?

Go Fyne 禁用 HSplit 调整大小?

Go Flag 用法 描述 包含 Word 值

Go:为一组单个结果实现 ManyDecode

在 go (1.18) 的泛型上实现多态的最佳方法是什么?

将函数的值作为输入参数返回给另一个