目录

正文

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(&amp;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(&amp;o.done, 1)
      f()
   }
}
doSlowdoneatomic.StoreUint32done

Mutex

doSlowMutexMutexfffMutexMutexfMutex

但是它们的阻塞状态被解除了,可以继续往下执行。

总结

OnceOnceDoOnce.DoffOnce.Dofpanicf
您可能感兴趣的文章: