我是新手,我正在努力了解goroutines中频道的工作方式。据我所知,关键字range
可用于迭代通道的值,直到通道关闭或缓冲区用完为止;因此,for range c
将重复循环,直到缓冲区用完为止。
我有以下简单的功能,可以为频道增加价值:
func main() {
c := make(chan int)
go printchannel(c)
for i:=0; i<10 ; i++ {
c <- i
}
}
我有printchannel
的两个实现,我不确定为什么行为不同。
实施1:
func printchannel(c chan int) {
for range c {
fmt.Println(<-c)
}
}
输出:1 3 5 7
实施2:
func printchannel(c chan int) {
for i:=range c {
fmt.Println(i)
}
}
输出:0 1 2 3 4 5 6 7 8
我期待这些输出都没有!
通缉输出:0 1 2 3 4 5 6 7 8 9
main
函数和printchannel
函数不能并行运行两个线程,一个向通道添加值,另一个在通道关闭之前读取值?我可能在这里缺少一些基本的go / thread概念,指向它会有所帮助。
非常感谢对此的反馈(以及我对goroutines中的频道操作的理解)!
答案 0 :(得分:3)
实施1.您已经从频道两次阅读 - range c
和<-c
都在从频道中读取。
实施2.这是正确的方法。您可能看不到9打印的原因是两个goroutine可能在并行线程中运行。在这种情况下,它可能是这样的:
如果你需要同步你的goroutines。例如,像这样
func printchannel(c chan int, wg *sync.WaitGroup) {
for i:=range c {
fmt.Println(i)
}
wg.Done() //notify that we're done here
}
func main() {
c := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1) //increase by one to wait for one goroutine to finish
//very important to do it here and not in the goroutine
//otherwise you get race condition
go printchannel(c, &wg) //very important to pass wg by reference
//sync.WaitGroup is a structure, passing it
//by value would produce incorrect results
for i:=0; i<10 ; i++ {
c <- i
}
close(c) //close the channel to terminate the range loop
wg.Wait() //wait for the goroutine to finish
}
关于goroutines vs threads。你不应该混淆它们,也许应该理解它们之间的区别。 Goroutines是绿线。有关该主题的无数博客文章,讲座和stackoverflow答案。
答案 1 :(得分:1)
你的第一次实施只返回其他所有数字的原因是因为你实际上是&#34;采取&#34;每次循环运行时c
两次:首先使用range
,然后再使用<-
。碰巧你实际上没有绑定或使用从频道取下的第一个值,所以你最后打印的都是其他的。
首次实施的另一种方法是根本不使用range
,例如:
func printchannel(c chan int) {
for {
fmt.Println(<-c)
}
}
我无法在我的机器上复制你的第二个实现的行为,但原因是你的两个实现都很活泼 - 它们将在主要结束时终止,无论通道中有哪些数据可能未决或者然而,许多goroutines可能是活跃的。
作为结束语,我警告你不要将goroutines视为明确存在&#34;线程&#34;,尽管他们有类似的心理模型和界面。在这样一个简单的程序中,Go可能不会使用单个OS线程完成所有操作。
答案 2 :(得分:1)
在实现1中,范围一次读入通道,然后再在Println中读取。因此,您正在跳过2,4,6,8。
在两种实现中,一旦最终的i(9)被发送到goroutine,程序就会退出。因此goroutine没有时间打印9.要解决它,请使用其他答案中提到的WaitGroup,或使用完成的通道来避免信号量/互斥量。
func main() {
c := make(chan int)
done := make(chan bool)
go printchannel(c, done)
for i:=0; i<10 ; i++ {
c <- i
}
close(c)
<- done
}
func printchannel(c chan int, done chan bool) {
for i := range c {
fmt.Println(i)
}
done <- true
}
答案 3 :(得分:-1)
您的第一个循环不起作用,因为您有2个阻塞通道接收器,并且它们不会同时执行。
当您调用goroutine时,循环开始,并等待第一个值发送到通道。有效地将其视为<-c
。
当主函数中的for循环运行时,它在Chan上发送0。此时,range c
接收该值并停止阻止循环的执行。
然后它被fmt.println(<-c)
的接收者阻止。当在主循环的第二次迭代中发送1时,接收到的fmt.println(<-c)
从通道读取,允许fmt.println
执行,从而完成循环并等待for range c
处的值
循环机制的第二个实现是正确的。
它在打印到9之前退出的原因是在main
中的for循环结束后,程序继续执行并完成main的执行。
在Go中,func main在执行时作为goroutine本身启动。因此,当main中的for循环完成时,它继续并退出,并且当打印在关闭的并行goroutine内时,它永远不会被执行。它没有时间打印,因为没有什么可以阻止主程序完成和退出程序。
解决此问题的一种方法是使用等待组http://www.golangprograms.com/go-language/concurrency.html
为了获得预期的结果,您需要在main中运行阻塞进程,以便在允许程序继续之前提供足够的时间或等待确认执行goroutine。