Concurrency Golang并发:如何从不同的goroutine附加到同一个切片

Concurrency Golang并发:如何从不同的goroutine附加到同一个切片,concurrency,go,append,goroutine,Concurrency,Go,Append,Goroutine,我有一个并发goroutine,它想将一个(指向某个)结构附加到同一个片上。 如何在Go中编写它以确保并发安全 这将是我的并发不安全代码,使用等待组: var wg sync.WaitGroup MySlice = make([]*MyStruct) for _, param := range params { wg.Add(1) go func(param string) { defer wg.Done() OneOfMyStructs := g

我有一个并发goroutine,它想将一个(指向某个)结构附加到同一个片上。 如何在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通道来实现并发安全。有人能举个例子吗?

频道是解决这个问题的最佳方式。下面是一个可以运行的示例

主程序包
输入“fmt”
导入“同步”
导入“运行时”
类型T int
func main(){
变量片[]T
var wg sync.WaitGroup
队列:=make(chant,1)
//创建数据并将其发送到队列中。
工作组.增补(100)
对于i:=0;i<100;i++{
go func(i int){
推迟工作组完成()
//做事。
runtime.Gosched()

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

如果
参数的大小固定:

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都写入不同的内存,这是不正常的。

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

主程序包
输入“fmt”
导入“同步”
类型T int
func main(){
变量片[]T
var wg sync.WaitGroup
队列:=make(chant,1)
//创建数据并将其发送到队列中。
工作组.增补(100)
对于i:=0;i<100;i++{
go func(i int){

//defer wg.Done()我相信这里的回答很好地回答了这个问题:这很有趣,您最后要考虑的是:如果切片的大小已知,并且您只是处理指向对象的指针,那么您根本不需要使用并发机制。这不依赖于“指针切片”:它也适用于“MyStruct的切片”。同样,代码从不写入相同的内存。我假设指针的内存分配是固定的,而结构的内存分配是不固定的。我想我错了。胡?什么是“固定的”"?Go中的任何类型都有一个完全由编译时决定的特定内存布局。指针和其他东西之间没有区别。我已经读到,除非有特殊需要,否则通常不应该在通道中使用缓冲。对于您关于goroutines写入共享通道的建议,您是否只需要同步交付(无缓冲)?为什么接收goroutine的通道需要调用defer wg.Done()?它不必延迟。只需一个
wg.Done()
在该goroutine结束时调用将在这种情况下起作用。当您有多个出口/返回时,延迟对于确保正确的行为非常有用。实际上我的问题是为什么
wg.Done()
需要在第二个go例程中调用吗?第一个循环将清除计数器100。原始代码中有一个错误。您实际上需要wg.Add(101)。您需要将其包括在等待组中,否则可能会导致其他所有goroutine都已完成,但在打印值之前尚未完成追加。此代码中存在错误。@ElgsQianChen
wg.Done()
在第二次执行例程中未被调用。@brendan自上次
wg.Done()以来
从不调用将计数设置为101将导致死锁。来自未来:@jimt解决方案为什么不起作用?
wg.Done()
defer
red,因此只有在通过通道发送值后才会调用它。我缺少什么?@chris我可以使用无缓冲通道吗?
队列:=make(chan T)
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)
}
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)
}