我有并发goroutine想要将(指向a)指针结构附加到同一个切片。 你如何在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渠道来实现并发安全。任何人都可以贡献一个例子吗?
答案 0 :(得分:20)
使用sync.Mutex保护MySlice = append(MySlice, &OneOfMyStructs)
没有任何问题。但是当然你可以有一个缓冲区大小为len(params)
的结果通道,所有goroutines都会发送他们的答案,一旦你的工作完成,你就从这个结果通道收集。
如果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)
}
由于所有goroutines写入不同的记忆,这不是生意。
答案 1 :(得分:12)
@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)
}
答案 2 :(得分:0)
渠道是解决这个问题的最佳方式。以下是可以在go playground上运行的示例。
package main
import "fmt"
import "sync"
import "runtime"
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()
// Do stuff.
runtime.Gosched()
queue <- T(i)
}(i)
}
// Poll the queue for data and append it to the slice.
// Since this happens synchronously and in the same
// goroutine/thread, this can be considered safe.
go func() {
defer wg.Done()
for t := range queue {
slice = append(slice, t)
}
}()
// Wait for everything to finish.
wg.Wait()
fmt.Println(slice)
}
注意:runtime.Gosched()
调用是存在的,因为这些goroutine不会产生调度程序。如果我们没有明确地做某事来触发所述调度程序,那么会导致死锁。另一种选择可能是执行一些I / O(例如:print to stdout)。但我发现runtime.Gosched()
的意图更容易,更清晰。