goroutine 和 Waitgroup 的问题
Issue with goroutine and Waitgroup
我正在尝试迭代一个循环并在匿名函数上调用 go 例程,并在每次迭代时添加一个等待组。并将字符串传递给相同的匿名函数并将值附加到切片 a。由于我正在循环 10000 次,因此切片的长度预计为 10000。但我看到了随机数。不确定是什么问题。谁能帮我解决这个问题?
这是我的代码片段
import (
"fmt"
"sync"
)
func main() {
var wg = new(sync.WaitGroup)
var a []string
for i := 0; i <= 10000; i++ {
wg.Add(1)
go func(s string) {
a = append(a, s)
wg.Done()
}("MaxPayne")
}
wg.Wait()
fmt.Println(len(a))
}
注意如何追加切片,实际上是创建一个新切片,然后将其分配回切片变量。所以你有不受控制的并发写入变量 a
。在 Go(和大多数语言)中并发写入相同的值是不安全的。为了安全起见,您可以使用互斥体对写入进行序列化。
尝试:
var lock sync.Mutex
var a []string
和
lock.Lock()
a = append(a, s)
lock.Unlock()
有关互斥锁工作原理的详细信息,请参阅 tour and the sync 包。
这里有一个模式可以实现类似的结果,但不需要互斥量并且仍然安全。
package main
import (
"fmt"
"sync"
)
func main() {
const sliceSize = 10000
var wg = new(sync.WaitGroup)
var a = make([]string, sliceSize)
for i := 0; i < sliceSize; i++ {
wg.Add(1)
go func(s string, index int) {
a[index] = s
wg.Done()
}("MaxPayne", i)
}
wg.Wait()
}
这与您的其他程序不完全相同,但这是它的作用。
- 创建一个已经具有所需大小 10,000 的切片(此时每个元素都是一个空字符串)
- 对于每个数字 0...9999,创建一个新的 goroutine,给定一个特定的索引,将特定的字符串写入
- 在所有goroutines都退出,等待组完成等待之后,我们就知道切片的每个索引都已经成功填充了。
即使没有互斥量,内存访问现在也是安全的,因为每个 goroutine 只写入它各自的索引(并且每个 goroutine 都有一个唯一的索引)。因此,none 这些并发内存写入相互冲突。在最初创建具有所需大小的切片后,变量 a
本身不需要再次分配,因此消除了原来的内存竞争。
我正在尝试迭代一个循环并在匿名函数上调用 go 例程,并在每次迭代时添加一个等待组。并将字符串传递给相同的匿名函数并将值附加到切片 a。由于我正在循环 10000 次,因此切片的长度预计为 10000。但我看到了随机数。不确定是什么问题。谁能帮我解决这个问题?
这是我的代码片段
import (
"fmt"
"sync"
)
func main() {
var wg = new(sync.WaitGroup)
var a []string
for i := 0; i <= 10000; i++ {
wg.Add(1)
go func(s string) {
a = append(a, s)
wg.Done()
}("MaxPayne")
}
wg.Wait()
fmt.Println(len(a))
}
注意如何追加切片,实际上是创建一个新切片,然后将其分配回切片变量。所以你有不受控制的并发写入变量 a
。在 Go(和大多数语言)中并发写入相同的值是不安全的。为了安全起见,您可以使用互斥体对写入进行序列化。
尝试:
var lock sync.Mutex
var a []string
和
lock.Lock()
a = append(a, s)
lock.Unlock()
有关互斥锁工作原理的详细信息,请参阅 tour and the sync 包。
这里有一个模式可以实现类似的结果,但不需要互斥量并且仍然安全。
package main
import (
"fmt"
"sync"
)
func main() {
const sliceSize = 10000
var wg = new(sync.WaitGroup)
var a = make([]string, sliceSize)
for i := 0; i < sliceSize; i++ {
wg.Add(1)
go func(s string, index int) {
a[index] = s
wg.Done()
}("MaxPayne", i)
}
wg.Wait()
}
这与您的其他程序不完全相同,但这是它的作用。
- 创建一个已经具有所需大小 10,000 的切片(此时每个元素都是一个空字符串)
- 对于每个数字 0...9999,创建一个新的 goroutine,给定一个特定的索引,将特定的字符串写入
- 在所有goroutines都退出,等待组完成等待之后,我们就知道切片的每个索引都已经成功填充了。
即使没有互斥量,内存访问现在也是安全的,因为每个 goroutine 只写入它各自的索引(并且每个 goroutine 都有一个唯一的索引)。因此,none 这些并发内存写入相互冲突。在最初创建具有所需大小的切片后,变量 a
本身不需要再次分配,因此消除了原来的内存竞争。