目录
正文
sync.OnceMutexsync.Once
Once 的实现
Once
package sync import ( "sync/atomic" ) type Once struct { done uint32 m Mutex } func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 0 { o.doSlow(f) } } func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }
简要说明:
doneDoDoOnceMutex
使用示例
once.Do(test)test
package sync import ( "fmt" "sync" "testing" ) var once sync.Once var a = 0 func test() { a++ } func TestOnce(t *testing.T) { var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { go func() { // once.Do 会调用 3 次,但最终只会执行一次 once.Do(test) wg.Done() }() } wg.Wait() fmt.Println(a) // 1 }
Once 的一些工作机制
OnceDoDoDoOnce.DoDoDoOnce.DopanicDoDoDoatomic.CompareAndSwapUint32f()f()OnceMutex
// 错误实现,这不能保证 f 只被执行一次 if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
Once.Dof
config.once.Do(func() { config.init(filename) })
Once 详解
hotpath
hotpathOncedone
type Once struct { // hotpath done uint32 m Mutex }
OncedonedoneOncemdone
结构体地址计算示例:
type person struct { name string age int } func TestStruct(t *testing.T) { var p = person{ name: "foo", age: 10, } // p 和 p.name 的地址相同 // 0xc0000100a8, 0xc0000100a8 fmt.Printf("%p, %p\n", &p, &p.name) // p.age 的地址 // 0xc0000100b8 fmt.Printf("%p\n", &p.age) // p.age 的地址也可以通过:结构体地址 + age 字段偏移量 计算得出。 // 0xc0000100b8 fmt.Println(unsafe.Add(unsafe.Pointer(&p), unsafe.Offsetof(p.age))) }
atomic.LoadUint32
func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 0 { o.doSlow(f) } }
Doatomic.LoadUint32donedone == 0doSlowdoneDoatomic.LoadUint32
doSlowdone == 0doSlowMutexdone = 1doSlowdone == 0
atomic.StoreUint32
func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }
doSlowdoneatomic.StoreUint32done
Mutex
doSlowMutexMutexfffMutexMutexfMutex
但是它们的阻塞状态被解除了,可以继续往下执行。
总结
OnceOnceDoOnce.DoffOnce.Dofpanicf
您可能感兴趣的文章: