英文:
Last value is missing when reading channel written in concurrent goroutines
问题
我是你的中文翻译助手,以下是你提供的代码的翻译:
我在Go语言中还比较新手,我想要异步运行多个任务,等待它们全部完成,并将它们的结果收集到一个切片中。
我阅读了很多文档和示例,特别是Nathan LeClaire的文章,并且根据自己的需求编写了类似的代码(见下方代码)。机制很简单:
- 触发了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] // 注意这里缺少了9
英文:
I'm fairly new in Go and I want to run several tasks asynchronously, wait for all of them to be finished and collect their results into a slice.
I was reading a lot of documentation and examples, specially this Nathan LeClaire's post, and came up with something close to what I want to do (see code below). The mechanic is simple:
- 10 goroutines are triggered and each of them writes a value in a channel.
- Another goroutine reads the channel and fills the slice.
- After all these stuff are done, the slice is printed.
However the outcome shows a 9-length slice (values from 0 to 8) and the 10th value (should be 9) seems missing. The program exits just fine and I don't know what is going on. Any hint is appreciated.
Here is a code example to play with 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)
}
And this is the outcome:
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
答案1
得分: 5
你在接收goroutine有机会接收和处理值之前就退出了。array
变量也存在竞争条件,main
可能会在append
操作期间尝试打印数组。
请注意,即使使用无缓冲通道会在两个循环之间创建一个同步点,并确保接收循环在wg.Done()
之前获得值,但它不能保证fmt.Println
和append
发生在main
继续之前。
一种安排的方法是将接收循环放在主函数中,并在其自己的goroutine中等待关闭c
通道:
go func() {
wg.Wait()
close(c)
}()
for val := range c {
fmt.Println("从通道接收:", val)
array = append(array, val)
}
http://play.golang.org/p/YReTVZtsUv
英文:
You're exiting before the receiving goroutine has a chance to receive and handle the value. There is also a race condition on the array
variable, where main
may try to print the array during the append
operation.
Note that even though an using unbuffered channel would create a synchronization point between the two loops, and guarantee that the receive loop has the value before wg.Done()
, it wouldn't guarantee that the fmt.Println
and append
happen before main
continues.
One way to arrange this is to put the receive loop in main, and wait on closing the c
chan
in it's own goroutine:
go func() {
wg.Wait()
close(c)
}()
for val := range c {
fmt.Println("Recieving from channel: ", val)
array = append(array, val)
}
答案2
得分: 2
很简单。在某个时刻,所有的值都被写入,waitGroup被释放,然后一个goroutine正在填充slice。由于waitGroup被释放,可能会在通道被排空到slice之前发生打印。
为了解决这个问题,将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("发送值到通道: ", val)
c <- val
}(i)
}
var array []int
go func() {
for val := range c {
fmt.Println("从通道接收: ", val)
array = append(array, val)
wg.Done()
}
}()
wg.Wait()
fmt.Println("数组: ", array)
}
在playground中有示例。
英文:
Pretty simple. At some point all the values are written, the waitGroup is released and a goroutine is filling the sice. Since the waitGroup is release its possible for the print to occur before the channel is drained into the slice.
To solve, move the wg.Done() into the reader to prevent the print happening before the draining.
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)
}
Example in playground
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论