我认为,一旦我启动了通道范围内的goroutine,我将发送的所有值都会被打印出来。
package main
import "fmt"
func main() {
c := make(chan int)
go (func(c chan int){
for v := range c {
fmt.Println(v)
}
})(c)
c <- 1
c <- 2
c <- 3
c <- 4
// c <- 5 // uncomment to send more values
// c <- 6
close(c)
}
但是,如果我发送奇数个值(例如,1,2和3),则会打印所有值。
如果我发送偶数个值(例如,1,2,3和4),则不会打印最后一个值。
似乎是频道创建行:
c := make(chan int)
添加不同大小的缓冲区时,更改范围表达式的行为:
(我发送4个值)
c := make(chan int) // prints 1,2,3
c := make(chan int, 1) // same behavior, prints 1,2,3
c := make(chan int, 2) // prints 1,2
c := make(chan int, 3) // prints 1,2,3
c := make(chan int, 4) // [no output]
c := make(chan int, 5) // [no output]
c := make(chan int, 20) // [no output]
为什么我发送偶数时没有收到最后一个值?
更多内容:
我也在离线测试,在64位Linux下编译。
我改变了一点程序:
package main
import (
"fmt"
)
func main() {
c := make(chan int)
cc := make(chan int)
p := func (c chan int){
for v := range c {
fmt.Println(v)
}
}
go p(c)
go p(cc)
c <- 1
c <- 2
c <- 3
c <- 4
// c <- 5
// c <- 6
cc <- 1000
// cc <- 2000
close(c)
close(cc)
}
如果我退出行cc <- 2000
,则会打印所有内容。但如果我把它留下注释,我只会得到1,2和3。
似乎是一个时间问题。我认为行cc <- 1000
将阻止主函数,直到所有通道都被读取。
答案 0 :(得分:4)
根据memory model,您正在考虑接近发送而不是发送:
关闭通道发生在返回零的接收之前 值因为频道已关闭。
因此,您可以确保在相应的循环终止之前完成这些紧密语句。因为你也知道必须在最后一次发送这些通道之后发生关闭语句(因为它们在同一个例行程序中),你知道除了最后发送的值之外的所有值都将保证打印。我认为你所期待的是接近行为更像是一个发送,所以循环被迫打印它的最后一个值。例如,如果用发送值-1替换close语句,这将保证将打印所有值(可能除-1之外)。是否打印-1不能得到保证。
显然这是对某些内容的简化,但我认为编写示例的“正确”方法是使用sync.WaitGroup。它非常简单,非常适合解雇几个常规程序并等待它们全部完成。以下是您重写的代码以使用WaitGroup:
package main
import (
"fmt"
"sync"
)
func main() {
c := make(chan int)
cc := make(chan int)
var wg sync.WaitGroup
p := func(c chan int) {
for v := range c {
fmt.Println(v)
}
wg.Done()
}
wg.Add(2)
go p(c)
go p(cc)
c <- 1
c <- 2
c <- 3
c <- 4
cc <- 1000
cc <- 2000
close(c)
close(cc)
wg.Wait()
}
答案 1 :(得分:3)
写下最后一行我认为我意识到问题所在。
当主要结束时,所有其他goroutine结束。
goroutine中的for
循环不是原子的。
第cc <- 1000
行阻止主要,但range
本身解锁它,主要死亡(并杀死goroutine)不允许fmt.Println
执行。