我正在浏览blog post以了解频道,并且我对2nd example有疑问。我在操场上将其修改为this,在其中我将更多项目放置在这样的频道中:
package main
import (
"fmt"
)
func main() {
n := 3
in := make(chan int)
out := make(chan int)
// We now supply 2 channels to the `multiplyByTwo` function
// One for sending data and one for receiving
go multiplyByTwo(in, out)
// We then send it data through the channel and wait for the result
in <- n
in <- 3
in <- 6
in <- 10
fmt.Println(<-out)
}
func multiplyByTwo(in <-chan int, out chan<- int) {
// This line is just to illustrate that there is code that is
// executed before we have to wait on the `in` channel
fmt.Println("Initializing goroutine...")
// The goroutine does not proceed until data is received on the `in` channel
num := <-in
// The rest is unchanged
result := num * 2
out <- result
}
但是这会引发错误:
Initializing goroutine...
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox639017164/prog.go:18 +0xe0
goroutine 6 [chan send]:
main.multiplyByTwo(0x430080, 0x4300c0)
/tmp/sandbox639017164/prog.go:34 +0xe0
created by main.main
/tmp/sandbox639017164/prog.go:14 +0xa0
我对此的解释是,通道应处理传入的数据,所以如果我只是向通道中简单添加更多内容,为什么会引发错误?我假设它也将传入其他数字,并通过该函数运行它们。
如果我没有输出通道就这样运行它:
package main
import (
"fmt"
)
func main() {
n := 3
in := make(chan int)
//out := make(chan int)
// We now supply 2 channels to the `multiplyByTwo` function
// One for sending data and one for receiving
go multiplyByTwo(in)
// We then send it data through the channel and wait for the result
in <- n
in <- 3
in <- 6
in <- 10
}
func multiplyByTwo(in <-chan int) {
// This line is just to illustrate that there is code that is
// executed before we have to wait on the `in` channel
fmt.Println("Initializing goroutine...")
// The goroutine does not proceed until data is received on the `in` channel
num := <-in
// The rest is unchanged
result := num * 2
fmt.Println(result)
}
它处理进入通道的第一个输入,但随后又出错。 fatal error: all goroutines are asleep - deadlock!
[如果愿意,您可以将通道视为一种邮箱(也许具有特殊的隐形传送能力,例如the game Portal中的门户)。
unbuffered通道是一个邮箱,没有任何空间容纳任何软件包。要使某人邮寄包裹(发送值),他们必须等到收件人的手从邮箱中戳出来。然后,他们可以将包裹放到手中,然后将包裹放回邮箱。如果有人在排队,则必须在其他人后面排队。
一个缓冲的通道是一个可以容纳一个或多个程序包的邮箱。要发送包裹,请排队(如果有)。当您到达生产线的顶端时,您可以看一下方框。如果有足够的空间容纳您的包裹,则将其放入并继续您的业务。如果没有,您可以等到有空位后,再将包裹放进去,继续您的生意。
所以有一个通用的发送模式:
同时,如果要从频道接收,则在需要时排队,就像发送一样。一旦到达生产线的最前面,您就可以从包装箱中取出包裹,或者在无缓冲通道的情况下,等着您的手伸出没有箱子的另一边,让别人来来并放入一些东西。
[类似地,每个goroutine都像一个人或Go gopher。它(或他或她或您喜欢的任何代词)可以在需要时排队,并将事物放入或从其中一种渠道中取出。您的程序以一个goroutine开头,该例程调用main
。
在您的代码中,您产生了第二个goroutine,该例程从multiplyByTwo
开始。这个goroutine等待-once-一个数字出现在通道中,或者在这种情况下,由于通道没有缓冲,有人正在等待send一个数字。然后,它将获得的(单个)数字加倍,打印结果,然后退出/死亡/被掩埋,再也不会存在。
与此同时,您的main
等待接收某人-这将是您的第二个goroutine-直到准备好接受3
中的数字n
为止。那部分成功。然后,您的main
等待另一个接收,以便它可以发送常量3
。
main
等待时,另一个goroutine正在执行其工作-或可能已完成其工作-并退出。现在,整个系统中只有一个“人”(或gopher或其他任何人),在等待另一个人(这个人不存在并且永远不会出生)来获得这个数字。底层的Go系统可以告诉您该事件永远不会发生,那就是您收到消息的时间:
fatal error: all goroutines are asleep - deadlock!
(这也会终止程序)。
Burak Serdar's answer显示如何从通道中获取第二个goroutine keep reading数字。这就引入了一个新的问题:如何告诉第二个goroutine没有更多的数字出现?答案是可以使用close
来关闭通道。
如果我们坚持使用邮箱的类比,您可以考虑关闭频道,就像在频道的发送侧放置了特殊的标签或标签一样。这样可以防止任何人进行任何进一步的投入。通道中的任何包裹都已经安全了,它们会一直呆在那里直到有人收到为止。但是new包裹不能进入。在接收方,很容易分辨出包裹和包裹之间的区别。这个特殊的标签:因此,当您遇到“封闭”标签时,您将不会再获得任何价值。如果通道没有缓冲,则可以立即看到此标签。如果已缓冲,则必须先取出所有现有的包装,然后才能看到它。通常,发送方 应该 nomain
的goroutine从其调用返回到main
,则所有其他goroutine或多或少会立即死亡。)请注意,一旦关闭,