Go 我可以同时写入不同的切片元素吗

Go 我可以同时写入不同的切片元素吗,go,concurrency,slice,goroutine,Go,Concurrency,Slice,Goroutine,我有一个切片,其中包含要完成的工作,还有一个切片,它将包含完成所有工作后的结果。以下是我的一般流程示意图: var results = make([]Result, len(jobs)) wg := sync.WaitGroup{} for i, job := range jobs { wg.Add(1) go func(i int, j job) { defer wg.Done() var r Result = doWork(j)

我有一个切片,其中包含要完成的工作,还有一个切片,它将包含完成所有工作后的结果。以下是我的一般流程示意图:

var results = make([]Result, len(jobs))
wg := sync.WaitGroup{}
for i, job := range jobs {
    wg.Add(1)
    go func(i int, j job) {
        defer wg.Done()
        var r Result = doWork(j)
        results[i] = r
    }(i, job)
}
wg.Wait()
// Use results
它似乎有效,但我还没有彻底测试过,也不确定这样做是否安全。一般来说,让多个goroutine写入任何内容都会让我感觉不好,但在这种情况下,每个goroutine都被限制在片中自己的索引中,这是预先分配的


我认为另一种选择是通过一个渠道收集结果,但由于结果的顺序很重要,这似乎相当简单。以这种方式写入切片元素安全吗?

规则很简单:如果多个goroutine同时访问一个,并且至少有一个访问是写入,则需要同步

你的例子没有违反这条规则。你不写切片值(切片头),你只读它(隐式的,当你索引它时)

您不读取切片元素,只修改切片元素。每个goroutine只修改一个单独的、不同的、指定的slice元素。由于每个slice元素都有自己的地址(自己的内存空间),它们就像是不同的变量。这包括在

和类型的结构化变量具有可以单独设置的元素和字段每个这样的元素都像一个变量。

必须记住的是,如果没有同步,您无法从
结果
切片读取结果。您在示例中使用的waitgroup是一个足够的同步。允许您读取切片一次
wg.Wait()
返回,因为这只能在所有工作程序goroutines调用
wg.Done()
之后发生,并且没有一个工作程序goroutines在调用
wg.Done()
之后修改元素

例如,这是检查/处理结果的有效(安全)方法:

wg.Wait()
// Safe to read results after the above synchronization point:
fmt.Println(results)
但是如果你想在
wg.Wait()
之前访问
结果的元素,那就是一场数据竞赛:

// This is data race! Goroutines might still run and modify elements of results!
fmt.Println(results)
wg.Wait()

是的,这是完全合法的:一个切片有一个数组作为它的底层数据存储,而作为一个复合类型,数组是一个“元素”序列,其行为就像具有不同内存位置的单个变量;同时修改它们是可以的

只需确保将worker goroutines的关闭与同步即可 在读取片的更新内容之前的主块

像您这样使用
sync.WaitGroup
进行此操作完全可以


另外,正如@icza所说,您不能修改切片值本身(它是一个包含指向备份存储阵列、容量和长度的指针的结构)。

是的,您可以。

太长,读不下去了 在示例中,它在中具有相同的示例代码

Google:=func(ctx-context.context,查询字符串)([]结果,错误){
g、 ctx:=errgroup.WithContext(ctx)
搜索:=[]搜索{Web、图像、视频}
结果:=make([]结果,len(搜索))
对于i,搜索:=范围搜索{
i、 搜索:=i,搜索
g、 Go(func()错误{
结果,错误:=搜索(ctx,查询)
如果err==nil{
结果[i]=结果
}
返回错误
})
}
如果错误:=g.Wait();错误!=nil{
返回零,错误
}
返回结果,无
}
// ...

哦,好吧,几乎是逐字逐句地回答@icza的问题;他抢先告诉我:-)请注意,您可以通过包装到测试中并通过
go-test-race-run-name-func运行它来快速检查此类代码。
-如果它检测到数据竞争,它会惊慌失措。