英文:
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 <- 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
}
}
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之间的通信是“同步的”和“无缓冲的”:发送操作只有在有接收者准备接收值时才会完成。必须有一个接收者准备从通道接收数据,然后发送者可以直接将其交给接收者。
因此,通道的发送/接收操作会阻塞,直到另一侧准备就绪为止:
-
通道上的发送操作会阻塞,直到同一通道上有接收者可用:如果
ch
上没有接收者,就无法将值放入通道中。反之亦然:当通道不为空时,无法发送新值到ch
中!因此,发送操作将等待,直到ch
再次可用。 -
通道的接收操作会阻塞,直到同一通道上有发送者可用:如果通道中没有值,接收者将被阻塞。
下面的示例说明了这一点:
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)
}
英文:
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 "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
}
}
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(<- 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 <- 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 <- num
处阻塞了。
你应该使用带缓冲的通道,像这样:c := make(chan int, 1)
在 Go playground 上尝试一下吧。
英文:
You use unbuffered channel, so your goroutine hang on c <- num
You should use buffered channel, like this: c := make(chan int, 1)
Try it on the <kbd>Go playground</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论