读取以并发 goroutine 编写的通道时缺少最后一个值
Last value is missing when reading channel written in concurrent goroutines
我是 Go 的新手,我想 运行 异步执行多个任务,等待所有任务完成并将它们的结果收集到一个切片中。
我阅读了很多文档和示例,特别是这个 Nathan LeClaire's post,并想出了一些接近我想做的事情(见下面的代码)。机制很简单:
- 触发了 10 个 goroutine,每个 goroutine 都在通道中写入一个值。
- 另一个 goroutine 读取通道并填充切片。
- 完成所有这些工作后,打印切片。
但是结果显示长度为 9 的切片(值从 0 到 8)并且第 10 个值(应该是 9)似乎丢失了。该程序退出得很好,我不知道发生了什么。任何提示表示赞赏。
这里有一个代码示例可以玩 http://play.golang.org/p/HUFOZLmCto:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
n := 10
c := make(chan int)
wg.Add(n)
for i := 0; i < n; i++ {
go func(val int) {
defer wg.Done()
fmt.Println("Sending value to channel: ", val)
c <- val
}(i)
}
var array []int
go func() {
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
}
}()
wg.Wait()
fmt.Println("Array: ", array)
}
这是结果:
Sending value to channel: 0
Recieving from channel: 0
Sending value to channel: 1
Recieving from channel: 1
Sending value to channel: 2
Recieving from channel: 2
Sending value to channel: 3
Recieving from channel: 3
Sending value to channel: 4
Recieving from channel: 4
Sending value to channel: 5
Recieving from channel: 5
Sending value to channel: 6
Recieving from channel: 6
Sending value to channel: 7
Recieving from channel: 7
Sending value to channel: 8
Recieving from channel: 8
Sending value to channel: 9
Array: [0 1 2 3 4 5 6 7 8] // Note 9 is missing here
您在接收 goroutine 有机会接收和处理该值之前退出。 array
变量也存在竞争条件,其中 main
可能会在 append
操作期间尝试打印数组。
请注意,即使使用无缓冲通道会在两个循环之间创建一个同步点,并保证接收循环具有 wg.Done()
之前的值,但不能保证 fmt.Println
和 append
发生在 main
继续之前。
安排这个的一种方法是将接收循环放在 main 中,并在它自己的 goroutine 中等待关闭 c
chan
:
go func() {
wg.Wait()
close(c)
}()
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
}
很简单。在某个时候所有的值都被写入,waitGroup 被释放并且一个 goroutine 正在填充 sice。由于 waitGroup 已释放,因此有可能在通道被排入切片之前进行打印。
要解决,将 wg.Done() 移动到 reader 以防止在排水之前发生打印。
package main
import (
"fmt"
"sync"
)
func main() {
n := 10
c := make(chan int)
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < n; i++ {
go func(val int) {
fmt.Println("Sending value to channel: ", val)
c <- val
}(i)
}
var array []int
go func() {
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
wg.Done()
}
}()
wg.Wait()
fmt.Println("Array: ", array)
}
中的例子
我是 Go 的新手,我想 运行 异步执行多个任务,等待所有任务完成并将它们的结果收集到一个切片中。
我阅读了很多文档和示例,特别是这个 Nathan LeClaire's post,并想出了一些接近我想做的事情(见下面的代码)。机制很简单:
- 触发了 10 个 goroutine,每个 goroutine 都在通道中写入一个值。
- 另一个 goroutine 读取通道并填充切片。
- 完成所有这些工作后,打印切片。
但是结果显示长度为 9 的切片(值从 0 到 8)并且第 10 个值(应该是 9)似乎丢失了。该程序退出得很好,我不知道发生了什么。任何提示表示赞赏。
这里有一个代码示例可以玩 http://play.golang.org/p/HUFOZLmCto:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
n := 10
c := make(chan int)
wg.Add(n)
for i := 0; i < n; i++ {
go func(val int) {
defer wg.Done()
fmt.Println("Sending value to channel: ", val)
c <- val
}(i)
}
var array []int
go func() {
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
}
}()
wg.Wait()
fmt.Println("Array: ", array)
}
这是结果:
Sending value to channel: 0
Recieving from channel: 0
Sending value to channel: 1
Recieving from channel: 1
Sending value to channel: 2
Recieving from channel: 2
Sending value to channel: 3
Recieving from channel: 3
Sending value to channel: 4
Recieving from channel: 4
Sending value to channel: 5
Recieving from channel: 5
Sending value to channel: 6
Recieving from channel: 6
Sending value to channel: 7
Recieving from channel: 7
Sending value to channel: 8
Recieving from channel: 8
Sending value to channel: 9
Array: [0 1 2 3 4 5 6 7 8] // Note 9 is missing here
您在接收 goroutine 有机会接收和处理该值之前退出。 array
变量也存在竞争条件,其中 main
可能会在 append
操作期间尝试打印数组。
请注意,即使使用无缓冲通道会在两个循环之间创建一个同步点,并保证接收循环具有 wg.Done()
之前的值,但不能保证 fmt.Println
和 append
发生在 main
继续之前。
安排这个的一种方法是将接收循环放在 main 中,并在它自己的 goroutine 中等待关闭 c
chan
:
go func() {
wg.Wait()
close(c)
}()
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
}
很简单。在某个时候所有的值都被写入,waitGroup 被释放并且一个 goroutine 正在填充 sice。由于 waitGroup 已释放,因此有可能在通道被排入切片之前进行打印。
要解决,将 wg.Done() 移动到 reader 以防止在排水之前发生打印。
package main
import (
"fmt"
"sync"
)
func main() {
n := 10
c := make(chan int)
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < n; i++ {
go func(val int) {
fmt.Println("Sending value to channel: ", val)
c <- val
}(i)
}
var array []int
go func() {
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
wg.Done()
}
}()
wg.Wait()
fmt.Println("Array: ", array)
}
中的例子