英文:
Noob on channels in Go
问题
我正在努力理解Go语言中的并发模式,并对来自#69的示例感到困惑。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
特别是,我不明白下面这段代码的工作原理:
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
因为我们只是创建了一个通道,现在我们就从它“接收”了10次?我尝试了其他代码,其中我创建了一个通道,然后立即尝试从它接收,但我总是得到一个错误,但这段代码似乎可以工作,我不太明白其中的原理。谢谢您的帮助!
英文:
I'm trying to wrap my head around concurrency patterns in Go and was confused by this example from #69
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
In particular, I don't see how
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
is supposed to work, since all we did was make the channel, and now we "receive" from it 10 times? I tried out other code where I create a channel and then try to receive from it right away and I always get an error, but this seems to work and I can't quite see how. Thanks for any help!
答案1
得分: 4
fmt.Println(<-c)
会阻塞,直到从通道中有可读取的内容。由于我们在一个单独的goroutine中启动了for
循环,这意味着循环的第一次迭代将只是空闲地等待,直到有可读取的内容。
然后fibonacci
函数开始,并将数据推送到通道中。这将唤醒循环并开始打印。
希望现在更清楚了。
英文:
fmt.Println(<-c)
will block until there's something to read from the channel. Since we start the for
loop in a separate goroutine, it means the first iteration of the loop will simply sit idly and wait until there's something to read.
Then the fibonacci
function starts, and pushes data down the channel. This will make the loop wake up and start printing.
I hope it makes better sense now.
答案2
得分: 2
我给你提供了一个更简洁的代码版本,我认为这样更容易理解(下面我会解释其中的区别)。请考虑以下代码:
// http://play.golang.org/p/5CrBSu4wxd
package main
import "fmt"
func fibonacci(c chan int) {
x, y := 0, 1
for {
c <- x
x, y = y, x+y
}
}
func main() {
c := make(chan int)
go fibonacci(c)
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
}
这个版本更直观,因为main
函数明显只是从通道中打印出10个值,然后退出;而后台的goroutine会在需要新值时填充通道。
这个替代版本去掉了_quit_通道,因为在这个简单的例子中,当main()
函数结束时,后台的goroutine会自动终止(不需要显式地杀死它)。
当然,这个版本也不再使用select{}
,这是#69的主题。但是,观察这两个版本如何完成相同的任务,除了终止后台goroutine之外,可能有助于理解select
的作用。
特别要注意的是,如果fibonacci()
函数的第一条语句是time.Sleep()
,那么for循环将会挂起相应的时间,但最终会继续工作。
希望对你有所帮助!
附注:刚意识到这个版本只是#68的一个简化版本,所以我不确定它能帮多少忙。糟糕。:-)
英文:
I’m giving you a shorter version of the code above, which I think should be easier to understand. (I explain the differences below.) Consider this:
// http://play.golang.org/p/5CrBSu4wxd
package main
import "fmt"
func fibonacci(c chan int) {
x, y := 0, 1
for {
c <- x
x, y = y, x+y
}
}
func main() {
c := make(chan int)
go fibonacci(c)
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
}
This is a more straightforward version because your main
function is clearly just printing 10 values from the channel, and then exiting; and there is a background goroutine that is filling the channel as long as a new value is needed.
This alternate version drops the quit channel, because the background goroutine simply dies when main()
finishes (no need to kill it explicitly in such a simple example).
Of course this version also kills the use of select{}
, which is the topic of #69. But seeing how both versions accomplish the same thing, except killing of the background goroutine, can perhaps be a good aid in understanding what select
is doing.
Note, in particular, that if fibonacci()
had a time.Sleep()
as its first statement, the for loop would hang for that much time, but would eventually work.
Hope this helps!
P.S.: Just realized this version is just a simpler version than #68, so I’m not sure how much it’ll help. Oops.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论