我有一个包含要完成的工作的 slice ,以及一个包含所有完成后的结果的 slice 。以下是我的大致流程示意图:
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 写入 anything,但在这种情况下,每个 goroutine 都仅限于 slice 中自己的索引,这是预先分配的。
我想另一种方法是通过 channel 收集结果,但由于结果的顺序很重要,这似乎相当简单。以这种方式写入 slice 元素是否安全?
最佳答案
规则很简单:如果多个 goroutine 访问一个 variable并发,且至少有一次访问是写,则需要同步。
您的示例没有违反此规则。你不写 slice value( slice 头),你只读取它(隐含地,当你索引它时)。
你不读取 slice 元素,你只修改 slice 元素。并且每个 goroutine 只修改一个 不同、指定的 slice 元素。而且由于每个 slice 元素都有自己的地址(自己的内存空间),它们就像不同的变量。这在 Spec: Variables: 中有介绍
Structured variables of array, slice, and struct types have elements and fields that may be addressed individually. Each such element acts like a variable.
必须记住的是,如果没有同步,您将无法从 results
slice 中读取结果。您在示例中使用的 WaitGroup 是足够的同步。一旦 wg.Wait()
返回,您就可以读取 slice ,因为这只能发生在所有调用 wg.Done()
的工作 goroutine 之后,并且没有一个工作goroutine 在调用 wg.Done()
之后修改元素。
例如,这是检查/处理结果的有效(安全)方式:
wg.Wait()
// Safe to read results after the above synchronization point:
fmt.Println(results)
但如果你试图在 wg.Wait()
之前访问 results
的元素,那就是数据竞争:
// This is data race! Goroutines might still run and modify elements of results!
fmt.Println(results)
wg.Wait()
关于go - 我可以同时编写不同的 slice 元素吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49879322/