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()
}
}
答案 0 :(得分:1)
Go memory model不保证您在done
中对if o.done == 1
的读取将自动进行。在这种情况下,程序的行为是不确定的。这有很多方法可能会出错-例如,您可能会读取在另一个goroutine中写出的部分值。
修改由多个goroutine同时访问的数据的程序必须对这种访问进行序列化。
要序列化访问,请使用通道操作或其他同步原语(例如
sync
和sync/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
$