我们应该在goroutine中同步变量分配吗? [英] Should we synchronize variable assignment in goroutine?

查看:75
本文介绍了我们应该在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)在对onetwo进行任何写操作之前都不会启动,因此在您的示例中互斥锁是不必要的.

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访问onetwo,那么是的,您需要将它们锁定,例如:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆