Синхронизация примитивов атомного пакета в Голанге - PullRequest
0 голосов
/ 25 апреля 2019

Синхронизировать пакет в golang у примитива Once.Метод Do () понял, что

func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 1 {
        return
    }
    // Slow-path.
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

Почему я не могу использовать другую версию этого метода?

func (o *Once) Do(f func()) {
    if o.done == 1 {
        return
    }
    // Slow-path.
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Ваша версия имеет гонку данных. Результаты не определены. Например,

racer.go * * 1004

package main

import (
    "sync"
    "sync/atomic"
    "time"
)

type Once struct {
    m    sync.Mutex
    done uint32
}

func (o *Once) Do(f func()) {
    if o.done == 1 {
        return
    }
    // Slow-path.
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

func main() {
    var once Once

    go once.Do(func() {})
    go once.Do(func() {})

    time.Sleep(1 * time.Second)
}

Выход:

$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c0000a0008 by goroutine 6:
  main.(*Once).Do()
      /home/peter/gopath/src/racer.go:15 +0x47

Previous write at 0x00c0000a0008 by goroutine 5:
  sync/atomic.StoreInt32()
      /home/peter/go/src/runtime/race_amd64.s:229 +0xb
  main.(*Once).Do()
      /home/peter/gopath/src/racer.go:25 +0x9f

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/racer.go:31 +0xc4

Goroutine 5 (finished) created at:
  main.main()
      /home/peter/gopath/src/racer.go:30 +0x96
==================
Found 1 data race(s)
exit status 66
$ 

См. Go: Data Race Detector .

1 голос
/ 25 апреля 2019

Модель памяти Go не гарантирует, что ваше чтение done в if o.done == 1 будет происходить атомарно. Поведение вашей программы не определено в этих обстоятельствах. Есть много причин, по которым это может пойти не так - например, вы можете прочитать частичное значение, записываемое в другой процедуре.

Программы, которые изменяют данные, к которым одновременно обращается несколько групп, должны сериализовать такой доступ.

Чтобы сериализовать доступ, защитите данные с помощью операций канала или других примитивов синхронизации, таких как в пакетах sync и sync/atomic.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...