我们应该在goroutine中同步变量分配吗? [英] Should we synchronize variable assignment in goroutine?
问题描述
假设我声明了两个映射,并希望将其分配给错误组中的两个不同的goroutine.我不执行任何读/写操作.
我应该使用lock
保护分配操作还是可以忽略它?
Let's assume I declared two maps and want to assign it in two different goroutines in error group. I don't perform any reads/write.
Should I protect assign operation with lock
or I can omit it?
UPD3:在 Java并发实践中 布莱恩·格茨(Brian Goetz)的第一部分第3章Shared Objects
,提到:
UPD3: In the Java Concurrency In Practice Brian Goetz's Part I Chapter 3 Shared Objects
, mentioned:
锁定不仅涉及互斥;这也是记忆 能见度.为确保所有线程看到最新值 共享的可变变量,读写线程必须 同步公用锁.
Locking is not just about mutual exclusion; it is also memory visibility. To ensure that all threads see the most up-to-date values of shared mutable variables, the reading and writing threads must synchronize on a common lock.
var (
mu sync.Mutex
one map[string]struct{}
two map[string]struct{}
)
g, gctx := errgroup.WithContext(ctx)
g.Go(func() error {
resp, err := invokeFirstService(gctx, request)
if err != nil {
return err
}
mu.Lock()
one = resp.One
mu.Unlock()
return nil
})
g.Go(func() error {
resp, err := invokeSecondService(gctx, request)
if err != nil {
return err
}
mu.Lock()
two = resp.Two
mu.Unlock()
return nil
})
if err := g.Wait(); err != nil {
return err
}
// UPD3: added lock and unlock section
m.Lock()
defer m.Unlock()
performAction(one, two)
UPD:添加了更多有关变量的上下文
UPD: added more context about variables
UPD2:我有什么疑问:我们有3个goroutines-父级和错误组中的两个.无法保证在errgroup goroutine完成后,我们的父goroutine共享内存会获得最后更新,直到我们用内存屏障包装对共享内存的访问为止
UPD2: what were my doubts: we have 3 goroutines - parent and two in the error group. there is no guarantee that our parent goroutine shared memory gets the last update after errgroup goroutines complete until we wrap access to shared memory with memory barriers
推荐答案
Group.Wait()
阻止,直到从 Group.Go()
方法已返回,因此这是一个同步点.这样可以确保performAction(one, two)
在对one
和two
进行任何写操作之前都不会启动,因此在您的示例中互斥锁是不必要的.
Group.Wait()
blocks until all function calls from the Group.Go()
method have returned, so this is a synchronization point. This ensures performAction(one, two)
will not start before any writes to one
and two
are done, so in your example the mutex is unnecessary.
g, gctx := errgroup.WithContext(ctx)
g.Go(func() error {
// ...
one = resp.One
return nil
})
g.Go(func() error {
// ...
two = resp.Two
return nil
})
if err := g.Wait(); err != nil {
return err
}
// Here you can access one and two safely:
performAction(one, two)
如果在写它们的goroutine同时运行时从其他goroutine访问one
和two
,那么是的,您需要将它们锁定,例如:
If you would access one
and two
from other goroutines while the goroutines that write them run concurrently, then yes, you would need to lock them, e.g.:
// This goroutine runs concurrently, so all concurrent access must be synchronized:
go func() {
mu.Lock()
fmt.Println(one, two)
mu.Unlock()
}()
g, gctx := errgroup.WithContext(ctx)
g.Go(func() error {
// ...
mu.Lock()
one = resp.One
mu.Unlock()
return nil
})
g.Go(func() error {
// ...
mu.Lock()
two = resp.Two
mu.Unlock()
return nil
})
if err := g.Wait(); err != nil {
return err
}
// Note that you don't need to lock here
// if the first concurrent goroutine only reads one and two.
performAction(one, two)
还要注意,在上面的示例中,您可以使用 sync.RWMutex
,然后在读取它们的goroutine, RWMutex.RLock()
和
Also note that in the above example you could use sync.RWMutex
, and in the goroutine that reads them, RWMutex.RLock()
and RWMutex.RUnlock()
would also be sufficient.
这篇关于我们应该在goroutine中同步变量分配吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!