7

golang源代码阅读,sync系列-Once

 2 years ago
source link: https://studygolang.com/articles/35036
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

golang源代码阅读,sync系列-Once

aseto · 8天之前 · 493 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    

csdn链接

  1. Once的作用为只执行函数一次。Once使用的场景并不多。因为初始化单例,一般是利用init函数,init函数也执行一次,但是init函数里面执行的东西建议是非阻塞性的,否则会影响整体程序的加载,且不利于定位问题,如果阻塞了话。阻塞性的可以使用Once,如配置初始化
  2. Once执行的函数是没有入参和返回参数的,所以一般会使用闭包的方式,初始化一些参数
  3. 因为Once的实例,一般是定义为全局变量,这样让某个函数只执行一次
  4. 跟sync下的Mutex、RWMutex一样,也是初始化后,不能copy,copy后新的也变量,再次做Do,也不会执行

     var once sync.Once
     once.Do(func() {
         fmt.Println("first")
     })
    
     twice := once
     twice.Do(func() {
         // 不会再次执行
         fmt.Println("second")
     })
    

    并且使用go vet main.go会有warning

     main.go:14:11: assignment copies lock value to twice: sync.Once contains sync.Mutex
    
  5. 函数只执行一次, 即使函数里面发生panic
var once sync.One
var a int
once.Init(func() {
    a = 1
})
type Once struct {
    // done indicates whether the action has been performed.
    // It is first in the struct because it is used in the hot path.
    // The hot path is inlined at every call site.
    // Placing done first allows more compact instructions on some architectures (amd64/386),
    // and fewer instructions (to calculate offset) on other architectures.
    // done确认是否已经执行过,标记位,done放在地位,在某些架构,能让指令更加紧凑(具体不太明白)
    done uint32
    // 用作确保Do的函数只执行一次
    m    Mutex
}
  1. 使用下面的方式实现的once action,虽然可以让函数f只执行一次,但是不能保证f是否已经完成,与Once的语义不一样, Once要求的是f执行完了,才能进入下一个Do
    if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
      f()
    }
    
  2. 函数f执行过程发生panic,外层会捕获,并且将done设置为1,保证函数f只执行一次,不管什么情况
func (o *Once) Do(f func()) {
    // Note: Here is an incorrect implementation of Do:
    //
    //  if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
    //      f()
    //  }
    //
    // Do guarantees that when it returns, f has finished.
    // This implementation would not implement that guarantee:
    // given two simultaneous calls, the winner of the cas would
    // call f, and the second would return immediately, without
    // waiting for the first's call to f to complete.
    // This is why the slow path falls back to a mutex, and why
    // the atomic.StoreUint32 must be delayed until after f returns.

    // 只有done是0的时候才能进入doSlow,可能有多个goroutine进入doSlow
    if atomic.LoadUint32(&o.done) == 0 {
        // Outlined slow-path to allow inlining of the fast-path.
        o.doSlow(f)
    }
}
func (o *Once) doSlow(f func()) {
    // 调用Lock,保证同时只有一个goroutine进入下面的代码
    o.m.Lock()
    // 整体执行完后,Unlock
    defer o.m.Unlock()
    // 一定要再次检查done,否则可能另个一goroutine进入执行f
    if o.done == 0 {
        // defer 表明如果函数f执行过程中出现panic,也会将donw置为1
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

有疑问加站长微信联系(非本文作者))

280

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK