【问题标题】:How to send and receive values to/from the channel within the same loop?如何在同一循环中向/从通道发送和接收值?
【发布时间】:2016-01-13 07:05:43
【问题描述】:

这是一个例子:

func main() {
    c := make(chan int)
    i := 0

    go goroutine(c)

    c <- i
    time.Sleep(10 * time.Second)

}

func goroutine(c chan int) {
    for {
        num := <- c
        fmt.Println(num)
        num++
        time.Sleep(1 * time.Second)
        c <- num
    }
}

我在 goroutine 内部尝试做的是从通道接收数字,打印它,递增,然后在一秒钟后将它发送回通道。之后我想重复这个动作。

但结果,操作只进行一次。

输出:

0

我做错了吗?

【问题讨论】:

    标签: go channel goroutine


    【解决方案1】:

    默认情况下,goroutine 通信是synchronousunbuffered:在有接收者接受该值之前,发送不会完成。必须有一个接收者准备好从通道接收数据,然后发送者可以将其直接交给接收者。

    所以通道发送/接收操作阻塞,直到对方准备好:

    1. 通道上的发送操作会阻塞,直到同一通道的接收者可用:如果ch 上的值没有接收者,则不能在通道中放入其他值.反之亦然:当通道不为空时,ch 不能发送新值!所以发送操作将等到ch 再次可用。

    2. 通道的接收操作会阻塞,直到发送方可用于同一通道:如果通道中没有值,则接收方会阻塞。

    这在以下示例中进行了说明:

    package main
    import "fmt"
    
    func main() {
        ch1 := make(chan int)
        go pump(ch1) // pump hangs
        fmt.Println(<-ch1) // prints only 0
    }
    
    func pump(ch chan int) {
        for i:= 0; ; i++ {
            ch <- i
        }
    }
    

    因为没有接收者,goroutine 挂起并且只打印第一个数字。

    为了解决这个问题,我们需要定义一个新的 goroutine,它在无限循环中从通道中读取。

    func receive(ch chan int) {
        for {
            fmt.Println(<- ch)
        }
    }
    

    然后在main():

    func main() {
        ch := make(chan int)
        go pump(ch)
        go receive(ch)
    }
    

    Go Playground

    【讨论】:

    • 感谢您的详细解释。
    • 请注意,您的最后一个 main() 函数在启动第二个 goroutine 后返回,因此程序可能会立即退出(它不会等待非main goroutines 完成)。可能是pump(),而receive() 甚至不会被调用。
    【解决方案2】:

    您使用

    创建一个无缓冲通道c
    c := make(chan int)
    

    在无缓冲通道上,操作是对称的,即通道上的每次发送都需要一次接收,每次接收都需要一次发送。您将i 发送到频道,goroutine 将其接收到num。之后,goroutine 将递增的num 发送到通道中,但没有人接收它。

    简而言之:声明

    c <- num
    

    会阻塞。

    您可以使用 1 缓冲通道,应该可以。

    您的代码还有另一个问题,您通过在main 中等待十秒钟解决了:您不知道您的 goroutine 何时结束。通常,sync.WaitGroup 用于这些情况。 但是:你的 goroutine 没有完成。在你的主 goroutine 中引入一个 chan struct{} 并在工作 goroutine 的两个通道上引入 select 是惯用的。

    【讨论】:

      【解决方案3】:

      你使用无缓冲通道,所以你的 goroutine 挂在c &lt;- num
      您应该使用缓冲通道,如下所示:c := make(chan int, 1)

      Go playground上试试

      【讨论】:

        猜你喜欢
        • 2015-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多