如何在同一个循环中向/从通道发送和接收值?

时间:2016-01-13 07:05:43

标签: go channel goroutine

以下是一个例子:

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

我做错了吗?

3 个答案:

答案 0 :(得分:2)

使用

创建无缓冲的频道c
c := make(chan int)

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

简而言之:声明

c <- num

会阻止。

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

您的代码还有另外一个问题,即您在main中等待十秒后就解决了这个问题:您不知道您的goroutine何时完成。通常,在这些情况下使用sync.WaitGroup但是:你的goroutine没有完成。在一段时间之后引入你在主goroutine中关闭的chan struct{},并且在工作人员goroutine中的两个频道上select,这是惯用的。

答案 1 :(得分:2)

默认情况下,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

答案 2 :(得分:1)

您使用无缓冲频道,因此您的goroutine挂在c <- num上 您应该使用缓冲通道,如下所示:c := make(chan int, 1)

Go playground

上试试