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()
}

这与您的其他程序不完全相同,但这是它的作用。

  1. 创建一个已经具有所需大小 10,000 的切片(此时每个元素都是一个空字符串)
  2. 对于每个数字 0...9999,创建一个新的 goroutine,给定一个特定的索引,将特定的字符串写入
  3. 在所有goroutines都退出,等待组完成等待之后,我们就知道切片的每个索引都已经成功填充了。

即使没有互斥量,内存访问现在也是安全的,因为每个 goroutine 只写入它各自的索引(并且每个 goroutine 都有一个唯一的索引)。因此,none 这些并发内存写入相互冲突。在最初创建具有所需大小的切片后,变量 a 本身不需要再次分配,因此消除了原来的内存竞争。