我正在浏览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等待多个通信操作。 一个选择块直到其中一个案例可以运行,然后执行该案例。如果有多个准备就绪,它会随机选择一个。“
但案件如何执行?据我所知,他们说:
案例:将x发送到频道c
案例:从退出接收
我想我明白第二个只有当quit有一个值时才会执行,这个函数稍后会在go func()中完成。但是检查的第一个案例是什么?另外,在go func()中,我们显然是从c打印值,但c在那个时候不应该有任何东西?我能想到的唯一解释是go func()以某种方式在调用fibonacci()之后执行。我猜它是一个我不完全理解的goroutine,它看起来就像魔术一样。
如果有人能够通过这段代码告诉我它在做什么,我会很感激。
答案 0 :(得分:8)
请记住,通道会阻塞,因此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
将永远产生更多的值(或者直到整数溢出),所以即使你有一个具有无限长缓冲区的魔术通道,它也永远不会传递给消费者。
答案 1 :(得分:1)
了解此代码示例有两点关键:
首先,让我们回顾一下无缓冲通道的工作方式。从documentation
如果通道未缓冲,则发送方阻塞,直到接收方收到 收到了价值。
请注意,代码示例中的两个通道c
和quit
都没有缓冲。
第二,当我们使用go
关键字启动新的goroutine时,执行将与其他例程并行进行。因此,在示例中,我们运行了两个go例程:由func main()
开始的例程,和go func()...
内部的func main()
开始的例程。
我在此处添加了一些内联注释,这些注释应该使事情更清楚: 包主 导入“ 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()
}
答案 2 :(得分:0)
你几乎得到了它。
go func()中的,我们显然是从c打印值,但c在那个时候不应该有任何东西?我能想到的唯一解释是go func()以某种方式在调用fibonacci()之后执行。我猜它是一个goroutine
是的,go关键字启动goroutine,因此 func()将与 fibonacci(c,quit)同时运行。 Println中通道的接收只会阻塞,直到有东西要接收