Go 同步原语原子包
Go have Once原语中的同步包。Do()方法实现了Go 同步原语原子包,go,Go,Go have 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
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()
}
}
如果o.done==1,则不能保证在中读取done
。在这些情况下,程序的行为是未定义的。有很多方法会出错——例如,您可能会读取另一个goroutine中写入的部分值
修改由多个goroutine同时访问的数据的程序必须序列化此类访问
要序列化访问,请使用通道操作或其他同步原语(如sync
和sync/atomic
包中的原语)保护数据
您的版本存在数据竞争。结果未定义。比如说,
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
$
请参阅。,因为这将在不同步的情况下并发读取done字段,这是未定义的行为。