如何在同一个循环中发送和接收值 to/from 通道?
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
我是不是做错了什么?
您使用无缓冲通道,所以您的 goroutine 挂起 c <- num
您应该使用缓冲通道,如下所示:c := make(chan int, 1)
上试用
您使用
创建了一个无缓冲通道 c
c := make(chan int)
在无缓冲的通道上,操作是对称的,即通道上的每个发送都只需要一个接收,每个接收只需要一个发送。您将 i
发送到通道中,goroutine 将其接收到 num
中。之后,goroutine 将递增的 num
发送到通道中,但是没有人在那里接收它。
简而言之:声明
c <- num
会阻止。
您可以使用 1 缓冲通道,应该可以。
您的代码还有另一个问题,您在 main
中通过等待十秒钟解决了这个问题:您不知道 goroutine 何时完成。通常,在这些情况下使用 sync.WaitGroup
。 但是:你的协程没有完成。引入一个 chan struct{}
是惯用的,你在一段时间后在你的主 goroutine 中关闭并在 worker goroutine 的两个通道上 select
。
默认情况下,goroutine 通信是 synchronous
和 unbuffered
:只有接收方接受该值,发送才会完成。必须有一个接收方准备好从通道接收数据,然后发送方可以将数据直接交给接收方。
所以通道 send/receive 操作阻塞,直到另一端准备就绪:
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)
}
这是一个例子:
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
我是不是做错了什么?
您使用无缓冲通道,所以您的 goroutine 挂起 c <- num
您应该使用缓冲通道,如下所示:c := make(chan int, 1)
您使用
创建了一个无缓冲通道c
c := make(chan int)
在无缓冲的通道上,操作是对称的,即通道上的每个发送都只需要一个接收,每个接收只需要一个发送。您将 i
发送到通道中,goroutine 将其接收到 num
中。之后,goroutine 将递增的 num
发送到通道中,但是没有人在那里接收它。
简而言之:声明
c <- num
会阻止。
您可以使用 1 缓冲通道,应该可以。
您的代码还有另一个问题,您在 main
中通过等待十秒钟解决了这个问题:您不知道 goroutine 何时完成。通常,在这些情况下使用 sync.WaitGroup
。 但是:你的协程没有完成。引入一个 chan struct{}
是惯用的,你在一段时间后在你的主 goroutine 中关闭并在 worker goroutine 的两个通道上 select
。
默认情况下,goroutine 通信是 synchronous
和 unbuffered
:只有接收方接受该值,发送才会完成。必须有一个接收方准备好从通道接收数据,然后发送方可以将数据直接交给接收方。
所以通道 send/receive 操作阻塞,直到另一端准备就绪:
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)
}