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

我在操场上对此进行了一些修改,我将更多项目放入频道中,如下所示:


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

}


它处理通道中的第一个输入,但随后再次出错。fatal error: all goroutines are asleep - deadlock!


手掌心
浏览 126回答 1
1回答

慕妹3146593

如果您愿意,您可以将频道视为一种邮箱(可能具有特殊的传送能力,例如游戏 Portal中的门户)。无缓冲通道是一个完全没有空间容纳任何包裹的邮箱。对于要邮寄包裹(发送价值)的人来说,他们必须等到收件人的手从邮箱中伸出来。然后,他们可以将包裹放入手中,手将退回邮箱,并带走包裹。如果有其他人在排队,您必须在其他人后面排队。缓冲通道是一个可以容纳一个或多个包的邮箱。要发送包裹,请排队(如果有的话)。当你到达队伍的最前面时,你可以看看这个盒子。如果有空间容纳您的包裹,您可以将其放入并继续处理您的事务。如果没有,您可以等到有空间,然后将包裹放入并继续处理您的业务。所以有一个通用的发送模式:如果有必要的话就排队吧。当您到达队列的头部时,如果有空间,请将包裹放入,否则等待空间,或者,对于无缓冲的通道,等待有人来到另一侧(接收)并把手伸进去接收。同时,如果您想从某个通道接收数据,则需要排队,就像发送一样。一旦你排在队伍的最前面,你就可以从盒子里取出一个包裹,或者——对于无缓冲的通道——把你的手伸出盒子的另一边,等待有人拿走。过来并在里面放一些东西。在这个类比中,每个 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 之间共享一个通道。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go