为了账号安全,请及时绑定邮箱和手机立即绑定

go教程选择语句

go教程选择语句

Go
江户川乱折腾 2021-12-07 14:57:40
我正在处理 tour.golang.org 上的示例,但遇到了这段我不太明白的代码:package mainimport "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 阻塞,直到它的一个 case 可以运行,然后它执行那个 case。如果多个 case 准备好了,它会随机选择一个。”但是这些案件是如何执行的呢?据我所知,他们说:案例:将 x 发送到通道 c案例:从退出接收我想我明白第二个只有在退出有一个值时才执行,这是稍后在 go func() 中完成的。但是第一个案例检查的是什么?此外,在 go func() 中,我们显然是从 c 打印值,但此时 c 不应该有任何内容吗?我能想到的唯一解释是 go func() 在调用 fibonacci() 之后以某种方式执行。我猜这是一个我也不完全理解的 goroutine,它看起来很神奇。如果有人可以通过此代码并告诉我它在做什么,我将不胜感激。
查看完整描述

3 回答

?
守着一只汪

TA贡献1872条经验 获得超3个赞

请记住,通道会阻塞,因此 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 并且我不能从退出中读取,直到我可以为止。”


然后在你的主进程中,你分离出另一个读取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将永远产生更多的值(或直到整数溢出),因此即使您有一个具有无限长缓冲区的魔术通道,它也永远不会到达消费者。


查看完整回答
反对 回复 2021-12-07
?
莫回无

TA贡献1865条经验 获得超7个赞

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

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

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

请注意,在代码示例两个通道,c并且quit是无缓冲。

其次,当我们使用go关键字启动一个新的 goroutine 时,执行将与其他例程并行发生。因此,在本例中,我们同时运行两个走程序:常规的启动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()

}


查看完整回答
反对 回复 2021-12-07
?
杨魅力

TA贡献1811条经验 获得超6个赞

你已经明白了。

在 go func() 中,我们显然是从 c 打印值,但是 c 中不应该有任何内容吗?我能想到的唯一解释是 go func() 在调用 fibonacci() 之后以某种方式执行。我猜这是一个 goroutine

是的, go 关键字启动了一个 goroutine,所以func()将与fibonacci(c, quit) 同时运行。从 Println 中的通道接收只是阻塞,直到有东西要接收


查看完整回答
反对 回复 2021-12-07
  • 3 回答
  • 0 关注
  • 158 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信