在Golang中同步基本体原子包

时间:2019-04-25 12:42:22

标签: go

golang中的同步包具有“一次”原语。 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 个答案:

答案 0 :(得分:1)

Go memory model不保证您在done中对if o.done == 1的读取将自动进行。在这种情况下,程序的行为是不确定的。这有很多方法可能会出错-例如,您可能会读取在另一个goroutine中写出的部分值。

  

修改由多个goroutine同时访问的数据的程序必须对这种访问进行序列化。

     

要序列化访问,请使用通道操作或其他同步原语(例如syncsync/atomic包中的那些)保护数据。

答案 1 :(得分:1)

您的版本有数据争用。结果是不确定的。例如,

racer.go

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