Goroutines 共享一个数组通道:试图解决数据竞争
Goroutines sharing an array channel : trying to solve data race
我尝试使用并行 goroutines 编写一个复杂的程序。这是我的第一个带有通道的程序;)每个 goroutine returns 一个数组,不幸的是,结果是 "random"。如果我 运行 10 次程序,我有 10 个不同的结果 :(
这是对我的程序的过度简化,结果很好(可能是因为它太简单了)但是当我 运行 它带有 -race 参数时,有 4 个数据竞争。
我尝试使用 close() 函数,但没有用。
你能帮我找出错误吗?非常感谢您!
package main
import "fmt"
import "sync"
import "strconv"
func cat_strings(a int, b string) []string{
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
var x []string
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
x = cat_strings(i, "var")
queue <- x
}(i)
}
//close(queue)
go func() {
defer wg.Done()
for t := range queue {
slice = append(slice, t...)
}
}()
wg.Wait()
fmt.Println(slice)
}
此修复有两个部分,不要在 goroutine 之间共享切片,然后在 main
中同步范围 queue
。
import (
"fmt"
"strconv"
"sync"
)
func cat_strings(a int, b string) []string {
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
queue <- cat_strings(i, "var")
}(i)
}
go func() {
wg.Wait()
close(queue)
}()
for t := range queue {
slice = append(slice, t...)
}
fmt.Println(slice)
}
您没有理由在 goroutine 之间共享额外的 x
切片。如果每个 goroutine 需要另一个切片,请为每个 goroutine 定义一个新切片。共享单个切片总是需要额外的同步。
另一个竞争是在从 queue
附加到 slice
切片的 goruoutine 和最后的 fmt.Println
之间。没有理由让它们并发,因为在读取所有值之前您不想打印,所以在打印最终值之前完全完成 for-range 循环。
我尝试使用并行 goroutines 编写一个复杂的程序。这是我的第一个带有通道的程序;)每个 goroutine returns 一个数组,不幸的是,结果是 "random"。如果我 运行 10 次程序,我有 10 个不同的结果 :(
这是对我的程序的过度简化,结果很好(可能是因为它太简单了)但是当我 运行 它带有 -race 参数时,有 4 个数据竞争。
我尝试使用 close() 函数,但没有用。
你能帮我找出错误吗?非常感谢您!
package main
import "fmt"
import "sync"
import "strconv"
func cat_strings(a int, b string) []string{
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
var x []string
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
x = cat_strings(i, "var")
queue <- x
}(i)
}
//close(queue)
go func() {
defer wg.Done()
for t := range queue {
slice = append(slice, t...)
}
}()
wg.Wait()
fmt.Println(slice)
}
此修复有两个部分,不要在 goroutine 之间共享切片,然后在 main
中同步范围 queue
。
import (
"fmt"
"strconv"
"sync"
)
func cat_strings(a int, b string) []string {
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
queue <- cat_strings(i, "var")
}(i)
}
go func() {
wg.Wait()
close(queue)
}()
for t := range queue {
slice = append(slice, t...)
}
fmt.Println(slice)
}
您没有理由在 goroutine 之间共享额外的 x
切片。如果每个 goroutine 需要另一个切片,请为每个 goroutine 定义一个新切片。共享单个切片总是需要额外的同步。
另一个竞争是在从 queue
附加到 slice
切片的 goruoutine 和最后的 fmt.Println
之间。没有理由让它们并发,因为在读取所有值之前您不想打印,所以在打印最终值之前完全完成 for-range 循环。