42

我有并发的 goroutines 想要将一个(指向一个)结构​​的(指针)附加到同一个切片。你如何在 Go 中编写它以使其并发安全?

这将是我的并发不安全代码,使用等待组:

var wg sync.WaitGroup
MySlice = make([]*MyStruct)
for _, param := range params {
    wg.Add(1)
    go func(param string) {
        defer wg.Done()
        OneOfMyStructs := getMyStruct(param)
        MySlice = append(MySlice, &OneOfMyStructs)
    }(param)
}
wg.Wait()

我想你需要使用 go 通道来保证并发安全。任何人都可以举个例子吗?

4

2 回答 2

46

MySlice = append(MySlice, &OneOfMyStructs)用 sync.Mutex保护 没有错。但是当然你可以有一个缓冲区大小的结果通道,len(params)所有 goroutine 都发送它们的答案,一旦你的工作完成,你就可以从这个结果通道中收集。

如果您params的尺寸固定:

MySlice = make([]*MyStruct, len(params))
for i, param := range params {
    wg.Add(1)
    go func(i int, param string) {
         defer wg.Done()
         OneOfMyStructs := getMyStruct(param)
         MySlice[i] = &OneOfMyStructs
     }(i, param)
}

由于所有 goroutine 都写入不同的内存,因此这并不奇怪。

于 2013-08-28T23:02:38.500 回答
25

@jimt 发布的答案不太正确,因为它错过了通道中发送的最后一个值,而最后一个值defer wg.Done()从未被调用。下面的片段有更正。

https://play.golang.org/p/7N4sxD-Bai

package main

import "fmt"
import "sync"

type T int

func main() {
    var slice []T
    var wg sync.WaitGroup

    queue := make(chan T, 1)

    // Create our data and send it into the queue.
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func(i int) {
            // defer wg.Done()  <- will result in the last int to be missed in the receiving channel
            queue <- T(i)
        }(i)
    }

    go func() {
        // defer wg.Done() <- Never gets called since the 100 `Done()` calls are made above, resulting in the `Wait()` to continue on before this is executed
        for t := range queue {
            slice = append(slice, t)
            wg.Done()   // ** move the `Done()` call here
        }
    }()

    wg.Wait()

    // now prints off all 100 int values
    fmt.Println(slice)
}
于 2016-08-21T14:38:11.877 回答