我可以同时写不同的切片元素吗
Can I concurrently write different slice elements
我有一个切片包含要完成的工作,还有一个切片包含完成所有工作后的结果。以下是我的大致流程示意图:
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 元素安全吗?
规则很简单:如果多个goroutines同时访问一个variable,并且至少有一个访问是写,那么就需要同步。
你的例子没有违反这条规则。你不写切片值(切片header),你只读它(隐含地,当你索引它时)。
您不读取切片元素,您只修改切片元素。并且每个 goroutine 仅修改单个 不同、指定 切片元素。由于每个 slice 元素都有自己的地址(自己的内存space),它们就像不同的变量。 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
切片中读取结果。您在示例中使用的等待组是足够的同步。您可以读取切片一次 wg.Wait()
returns,因为这只会发生在所有调用 wg.Done()
的 worker goroutines 和 none worker goroutines 修改元素之后称为 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()
是的,这是完全合法的:一个切片有一个数组作为它的底层数据存储,并且,作为一种复合类型,数组是一个 "elements" 的序列,它表现为具有不同内存位置的独立变量;同时修改它们是可以的。
只需确保将您的 worker goroutines 的关闭与
读取切片更新内容之前的主要部分。
为此使用 sync.WaitGroup
非常好。
此外,正如@icza 所说,您不得修改切片值本身(这是一个包含指向后备存储数组、容量和长度的指针的结构)。
是的,你可以。
tldr
在golang.org/x/sync/errgroup example, it has the same example code in Example (Parallel)
Google := func(ctx context.Context, query string) ([]Result, error) {
g, ctx := errgroup.WithContext(ctx)
searches := []Search{Web, Image, Video}
results := make([]Result, len(searches))
for i, search := range searches {
i, search := i, search
g.Go(func() error {
result, err := search(ctx, query)
if err == nil {
results[i] = result
}
return err
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
// ...
我有一个切片包含要完成的工作,还有一个切片包含完成所有工作后的结果。以下是我的大致流程示意图:
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 元素安全吗?
规则很简单:如果多个goroutines同时访问一个variable,并且至少有一个访问是写,那么就需要同步。
你的例子没有违反这条规则。你不写切片值(切片header),你只读它(隐含地,当你索引它时)。
您不读取切片元素,您只修改切片元素。并且每个 goroutine 仅修改单个 不同、指定 切片元素。由于每个 slice 元素都有自己的地址(自己的内存space),它们就像不同的变量。 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
切片中读取结果。您在示例中使用的等待组是足够的同步。您可以读取切片一次 wg.Wait()
returns,因为这只会发生在所有调用 wg.Done()
的 worker goroutines 和 none worker goroutines 修改元素之后称为 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()
是的,这是完全合法的:一个切片有一个数组作为它的底层数据存储,并且,作为一种复合类型,数组是一个 "elements" 的序列,它表现为具有不同内存位置的独立变量;同时修改它们是可以的。
只需确保将您的 worker goroutines 的关闭与 读取切片更新内容之前的主要部分。
为此使用 sync.WaitGroup
非常好。
此外,正如@icza 所说,您不得修改切片值本身(这是一个包含指向后备存储数组、容量和长度的指针的结构)。
是的,你可以。
tldr
在golang.org/x/sync/errgroup example, it has the same example code in Example (Parallel)
Google := func(ctx context.Context, query string) ([]Result, error) {
g, ctx := errgroup.WithContext(ctx)
searches := []Search{Web, Image, Video}
results := make([]Result, len(searches))
for i, search := range searches {
i, search := i, search
g.Go(func() error {
result, err := search(ctx, query)
if err == nil {
results[i] = result
}
return err
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
// ...