Должны ли мы синхронизировать присваивание переменных в программе? - PullRequest
0 голосов
/ 19 марта 2019

Давайте предположим, что я объявил две карты и хочу назначить их в две разные программы в группе ошибок.Я не выполняю никаких операций чтения / записи.Должен ли я защищать операцию присваивания с помощью lock или я могу ее опустить?

UPD3: на практике Параллелизм Java Брайан Гетц Часть I Глава 3 Shared ObjectsУпоминается:

Блокировка - это не только взаимное исключение;это также видимость памяти.Чтобы все потоки могли видеть самые последние значения общих изменяемых переменных, потоки чтения и записи должны синхронизироваться с общей блокировкой.

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: добавлен дополнительный контекст о переменных

UPD2: какие у меня были сомнения: у нас есть 3 программы - родительская и две в группе ошибок.нет никакой гарантии, что наша совместно используемая память для родительских групп получит последнее обновление после завершения процедур errgroup, пока мы не закроем доступ к общей памяти барьерами памяти

Ответы [ 2 ]

3 голосов
/ 19 марта 2019

Group.Wait() блокируется до тех пор, пока не будут возвращены все вызовы функций из метода Group.Go(), так что это точка синхронизации.Это гарантирует, что performAction(one, two) не запустится до того, как будут выполнены записи в one и two, поэтому в вашем примере мьютекс не нужен.

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)

Если вы хотите получить доступ к one и two из других программ, в то время как программы, которые их пишут, работают одновременно, тогда да, вам нужно заблокировать их, например:

// 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, а в программе, которая их читает, RWMutex.RLock() и RWMutex.RUnlock() также будет достаточно.

0 голосов
/ 19 марта 2019

В этом случае только одна программа может получить доступ к карте.Я не думаю, что вам нужен замок.

...