Sync package in golang have Once primitive. Do() method realized that
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()
}
}
Why i cant use other version this method?
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()
}
}
The Go memory model does not guarantee that your read of done
in if o.done == 1
will proceed atomically. Your program's behaviour is undefined in these circumstances. There are many ways this can go wrong – for example, you may read a partial value being written out in another goroutine.
Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access.
To serialize access, protect the data with channel operations or other synchronization primitives such as those in the
sync
andsync/atomic
packages.
Your version has a data race. The results are undefined. For example,
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)
}
Output:
$ 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
$