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

huangapple go评论85阅读模式
英文:

How to send and receive values to/from the channel within the same loop?

问题

这是一个示例:

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

我做错了什么吗?

英文:

Here is an example:

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

    go goroutine(c)

    c &lt;- i
    time.Sleep(10 * time.Second)

}

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

What I'm trying to do inside of goroutine is to receive number from channel, print it, increment and after one second send it back to the channel. After this I want to repeat the action.

But as a result, operation is only done once.

Output:

0

Am I doing something wrong?

答案1

得分: 7

默认情况下,goroutine之间的通信是“同步的”和“无缓冲的”:发送操作只有在有接收者准备接收值时才会完成。必须有一个接收者准备从通道接收数据,然后发送者可以直接将其交给接收者。

因此,通道的发送/接收操作会阻塞,直到另一侧准备就绪为止:

  1. 通道上的发送操作会阻塞,直到同一通道上有接收者可用:如果ch上没有接收者,就无法将值放入通道中。反之亦然:当通道不为空时,无法发送新值到ch中!因此,发送操作将等待,直到ch再次可用。

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

下面的示例说明了这一点:

package main
import "fmt"

func main() {
    ch1 := make(chan int)
    go pump(ch1) // pump hangs
    fmt.Println(<-ch1) // 仅打印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

英文:

By default the goroutine communication is synchronous and unbuffered: sends do not complete until there is a receiver to accept the value. There must be a receiver ready to receive data from the channel and then the sender can hand it over directly to the receiver.

So channel send/receive operations block until the other side is ready:

1. A send operation on a channel blocks until a receiver is available for the same channel: if there’s no recipient for the value on ch, no other value can be put in the channel. And the other way around: no new value can be sent in ch when the channel is not empty! So the send operation will wait until ch becomes available again.

2. A receive operation for a channel blocks until a sender is available for the same channel: if there is no value in the channel, the receiver blocks.

This is illustrated in the below example:

package main
import &quot;fmt&quot;

func main() {
	ch1 := make(chan int)
	go pump(ch1) // pump hangs
	fmt.Println(&lt;-ch1) // prints only 0
}

func pump(ch chan int) {
	for i:= 0; ; i++ {
		ch &lt;- i
	}
}

Because there is no receiver the goroutine hangs and print only the first number.

To workaround this we need to define a new goroutine which reads from the channel in an infinite loop.

func receive(ch chan int) {
	for {
		fmt.Println(&lt;- ch)
	}
}

Then in main():

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

<kbd>Go Playground</kbd>

答案2

得分: 2

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

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

简而言之:语句c <- num会阻塞。

你可以使用一个有缓冲的通道,这样应该可以解决问题。

你的代码还存在另一个问题,你通过在main函数中等待十秒来解决:你不知道goroutine何时结束。通常,在这种情况下会使用sync.WaitGroup。但是:你的goroutine没有结束。惯用的做法是在主goroutine中引入一个chan struct{},在一段时间后关闭它,并在工作goroutine中使用select同时监听两个通道。

英文:

You create an unbuffered channel c with

c := make(chan int)

On unbuffered channels, operations are symmentrical, i.e. every send on a channel needs exactly one receive, every receive needs exactly one send. You send your i into the channel, the goroutine receives it into num. After that, the goroutine sends the incremented num into the channel, but nobody is there to receive it.

In short: The statement

c &lt;- num

will block.

You could use a 1-buffered channel, that should work.

There is another problem with your code that you solved by waiting ten seconds in main: You don't know when your goroutine finishes. Typically, a sync.WaitGroup is used in these situations. But: Your goroutine does not finish. It's idiomatic to introduce a chan struct{} that you close in your main goroutine after a while and select over both channels in the worker goroutine.

答案3

得分: 1

你使用的是非缓冲通道,所以你的 goroutine 在 c &lt;- num 处阻塞了。
你应该使用带缓冲的通道,像这样:c := make(chan int, 1)

Go playground 上尝试一下吧。

英文:

You use unbuffered channel, so your goroutine hang on c &lt;- num
You should use buffered channel, like this: c := make(chan int, 1)

Try it on the <kbd>Go playground</kbd>

huangapple
  • 本文由 发表于 2016年1月13日 15:05:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/34760364.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定