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

另一个 golang 频道关于理解它如何处理的问题

另一个 golang 频道关于理解它如何处理的问题

Go
手掌心 2023-07-31 17:11:43
我在操场上对此进行了一些修改,我将更多项目放入频道中,如下所示:package mainimport (    "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 +0xe0goroutine 6 [chan send]:main.multiplyByTwo(0x430080, 0x4300c0)    /tmp/sandbox639017164/prog.go:34 +0xe0created by main.main    /tmp/sandbox639017164/prog.go:14 +0xa0我对此的解释是通道应该处理传入的数据,那么如果我只是简单地向通道添加更多数据,为什么会抛出错误呢?我假设它也会传递其他数字并通过该函数运行这些数字。如果我在没有输出通道的情况下像这样运行它:package mainimport (    "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}它处理通道中的第一个输入,但随后再次出错。fatal error: all goroutines are asleep - deadlock!
查看完整描述

1 回答

?
慕妹3146593

TA贡献1820条经验 获得超9个赞

如果您愿意,您可以将频道视为一种邮箱(可能具有特殊的传送能力,例如游戏 Portal中的门户)。

缓冲通道是一个完全没有空间容纳任何包裹的邮箱。对于要邮寄包裹(发送价值)的人来说,他们必须等到收件人的手从邮箱中伸出来。然后,他们可以将包裹放入手中,手将退回邮箱,并带走包裹。如果有其他人在排队,您必须在其他人后面排队。

缓冲通道是一个可以容纳一个或多个包的邮箱。要发送包裹,请排队(如果有的话)。当你到达队伍的最前面时,你可以看看这个盒子。如果有空间容纳您的包裹,您可以将其放入并继续处理您的事务。如果没有,您可以等到有空间,然后将包裹放入并继续处理您的业务。

所以有一个通用的发送模式:

  1. 如果有必要的话就排队吧。

  2. 当您到达队列的头部时,如果有空间,请将包裹放入,否则等待空间,或者,对于无缓冲的通道,等待有人来到另一侧(接收)并把手伸进去接收。

同时,如果您想从某个通道接收数据,则需要排队,就像发送一样。一旦你排在队伍的最前面,你就可以从盒子里取出一个包裹,或者——对于无缓冲的通道——把你的手伸出盒子的另一边,等待有人拿走。过来并在里面放一些东西。

在这个类比中,每个 goroutine 就像一个人,或者一个Go gopher。如果需要,它(或他或她或您喜欢的任何代词)可以排队,并将东西放入这些通道之一或将其从这些通道之一中取出。你的程序从一个 goroutine 开始,它调用main.

在您的代码中,您派生出第二个 goroutine,它从 开始multiplyByTwo。这个 goroutine 等待一次,等待一个数字出现在通道中,或者在本例中,等待某人发送一个数字,因为通道没有缓冲。然后它将得到的(单个)数字加倍,打印结果,然后退出/死亡/被埋葬,永远不再存在。

同时,您main等待某人接收(这将是您的第二个 goroutine),直到它准备好接受3中的数字n。那部分成功了。然后你main等待另一个接收,以便它可以发送常量3

当你main等待的时候,你的另一个 goroutine 正在做它的工作——或者可能已经完成它的工作——然后退出。现在,整个系统中只有一个“人”(或地鼠或其他什么),等待着第二个人——这个人不存在,也永远不会出生——来取号。底层 Go 系统可以判断该事件永远不会发生,此时您会收到消息:

fatal error: all goroutines are asleep - deadlock!

(这也会终止程序)。

这引入了一个新问题:如何告诉第二个 goroutine 不再有数字到来?答案是您可以关闭通道,使用close.

如果我们坚持用邮箱进行类比,您可以将关闭通道视为在通道的发送端放置特殊的贴纸或标签。这可以防止任何人进一步输入值。通道中的任何包裹都已经是安全的——它们会一直留在那里,直到有人收到它们——但没有新的包裹可以进入。在接收方,很容易区分包裹和这个特殊贴纸之间的区别:所以当您遇到“关闭”标签,您知道不会有更多的值出现。如果频道没有缓冲,您可以立即看到此贴纸。如果它是缓冲的,您必须先取出所有现有的包,然后才能看到它。

一般来说,发送者应该关闭通道,以便接收者知道他们不会从中得到任何东西。(在许多特定情况下,您可以在不关闭通道的情况下逃脱。特别是,如果正在运行的 goroutinemain从其对 的调用返回main,则所有其他 goroutine 或多或少会立即死亡。)

请注意,一旦关闭,任何发送者都无法再次关闭该通道,因此这意味着,如果您有多个发送者共享的单个通道,则只有其中一个可以关闭该通道!使这一工作正常进行是很棘手的,因此更常见的是避免像这样在多个 write-goroutine 之间共享一个通道。


查看完整回答
反对 回复 2023-07-31
  • 1 回答
  • 0 关注
  • 60 浏览
慕课专栏
更多

添加回答

举报

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