目录
正文
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
您可能感兴趣的文章: