美文网首页
golang sync.Once源代码阅读--让你的初始化只执行

golang sync.Once源代码阅读--让你的初始化只执行

作者: guonaihong | 来源:发表于2020-02-28 21:48 被阅读0次

sync.Once的用处

sync.Once 主要用途执行只需要执行一次的初始化函数,比如验证器的初始,http.Client初始化,都可以派上大用场。

来个直观的例子

下面的例子比较贴近实战代码,你提供了一个包给别人使用,这个包里面有初始化函数。他对你提供的初始化函数和方法二次封装。只暴露一个函数出来,这时候就可以使用sync.Once包下初始化函数。

package main

import (
    "fmt"
    "sync"
)

type Client struct {
    url string
}

func New() *Client {
    return &Client{}
}

func (c *Client) Look(id int) {
    fmt.Printf("url is %d\n", id)
}

var (
    defaultClient *Client
    once          sync.Once
)

func look(id int) {
    once.Do(func() {
        defaultClient = New()
        fmt.Printf("init ok\n")
    })

    defaultClient.Look(id)
}
func main() {

    for i := 0; i < 5; i++ {
        look(i)
    }

}

sync.Once数据结构

done uint32标记位。如果用户的函数初始化过,标记位置为1。
m Mutext 锁的作用,是锁住回调函数的执行过程,第一个函数在资源没准备好时,第二个函数过来就要乖乖地等待。

// Once is an object that will perform exactly one action.
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/x86),
    // and fewer instructions (to calculate offset) on other architectures.
    done uint32
    m    Mutex
}

实现

  • if atomic.LoadUint32(&o.done) == 0 ,是if o.Done == 0的线程安全写法,如果 o.done等于0才会进下来的逻辑,初始化过自然就不会进的。 atomic.LoadUint32是原子变量取值函数,其实atomic下面包是汇编指令的抽象实现,不同平台会生成该平台对应机器指令,。
  • defer atomic.StoreUint32(&o.done, 1)o.done = 1线程安全写法。当然取值的时候也是要用atomic.LoadUint32取值才有效果的,如果写的时候用atomic.StoreUint32,取值直接用if o.done == 1等于没用。
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.

    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()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

注意(错误的实现)

这段是注释里面特别说明的,很多cas狂魔闭眼就能写出如下代码。这段代码其实有问题,
回调函数还没有初始化成功,这时第二个调用过来立马返回继续执行,资源没准备好就往下跑,panic少不了。

 
    if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
          f()
     }
    

相关文章

  • golang sync.Once源代码阅读--让你的初始化只执行

    sync.Once的用处 sync.Once 主要用途执行只需要执行一次的初始化函数,比如验证器的初始,http....

  • Go语言——sync.Once分析

    Go语言——sync.Once分析 sync.Once表示只执行一次函数。要做到这点,就需要两点: 计数器,统计函...

  • Golang之Mutex

    引用 sync.mutex 源代码分析 Golang 中 Mutex 的源码实现 建议阅读 Golang中Mute...

  • golang熟练运用sync.Once

    sync.Once的作用 控制需要执行的代码在执行时候有且只执行一次,经常用在单例模式下,保证对象只生成一次。注意...

  • sync.Once

    利用 sync.once 实现单例 sync.Once 实现 为什么要用defer 来加计数?不直接在后面执行计数...

  • 深入源码分析golang之sync.Once

    什么是sync.Once 官方文档对它的描述是:一个对象将完全执行一次,不得复制。常常用来单例对象的初始化场景,或...

  • golang的sync.Once

    很早就知道sync.Once,一直以来都觉得这个功能用处不大,所以就没关注,在项目中也没使用过它。原因很简单,可以...

  • sync.Once

    sync.Once 的使用场景 sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式...

  • go 中的 sync.Once

    sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式,sync.Once 仅提供了一...

  • java反射机制

    一、java类初始化 初始化操作:按照源代码从上到下的顺序依次执行静态代码块和初始化静态域。在一个类被初始化前,其...

网友评论

      本文标题:golang sync.Once源代码阅读--让你的初始化只执行

      本文链接:https://www.haomeiwen.com/subject/lfurhhtx.html