另一个golang频道提出有关了解其处理方式的问题

问题描述 投票:-4回答:2

我正在浏览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!

go
2个回答
1
投票

[如果愿意,您可以将通道视为一种邮箱(也许具有特殊的隐形传送能力,例如the game Portal中的门户)。

unbuffered通道是一个邮箱,没有任何空间容纳任何软件包。要使某人邮寄包裹(发送值),他们必须等到收件人的手从邮箱中戳出来。然后,他们可以将包裹放到手中,然后将包裹放回邮箱。如果有人在排队,则必须在其他人后面排队。

一个缓冲的通道是一个可以容纳一个或多个程序包的邮箱。要发送包裹,请排队(如果有)。当您到达生产线的顶端时,您可以看一下方框。如果有足够的空间容纳您的包裹,则将其放入并继续您的业务。如果没有,您可以等到有空位后,再将包裹放进去,继续您的生意。

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

  1. 如果需要,请排队。
  2. [当您到达队列的开头时,请在有空位的情况下将包裹放入,否则要等待空位;或者在无缓冲通道的情况下,让某人到达另一端(接收)并把手伸入接收。

同时,如果要从频道接收,则在需要时排队,就像发送一样。一旦到达生产线的最前面,您就可以从包装箱中取出包裹,或者在无缓冲通道的情况下,等着您的手伸出没有箱子的另一边,让别人来来并放入一些东西。

[类似地,每个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包裹不能进入。在接收方,很容易分辨出包裹和包裹之间的区别。这个特殊的标签:因此,当您遇到“封闭”标签时,您将不会再获得任何价值。如果通道没有缓冲,则可以立即看到此标签。如果已缓冲,则必须先取出所有现有的包装,然后才能看到它。通常,发送方

应该

关闭通道,以使接收方知道他们将无法从中获得更多收益。 (在许多特定情况下,您可以不关闭通道而逃脱。特别是,如果正在运行main的goroutine从其调用返回到main,则所有其他goroutine或多或少会立即死亡。)请注意,一旦关闭,

no

发件人可以关闭频道再次,因此这意味着,如果您有一个跨多个发件人share的单个频道,则只能关闭其中一个这个频道!正确地进行这项工作很棘手,因此更常见的是避免像这样跨多个写作专家共享一个频道。


3
投票
goroutine处理一个值,然后终止。您只能将第一个值发送到您的goroutine,此后goroutine消失了,没有人在收听您的频道。这就是为什么您会陷入僵局,并试图将数据发送到没有侦听器的频道。
© www.soinside.com 2019 - 2024. All rights reserved.