关于后端:深入源码分析golang之syncOnce

什么是sync.Once

  官网文档对它的形容是:一个对象将齐全执行一次,不得复制。经常用来单例对象的初始化场景,或者并发拜访只须要初始化一次的共享资源。sync.Once只裸露了一个办法Do,能够屡次调用,然而只有第一次调用Do办法时f参数才会执行,这里的f是一个无参数无返回值的函数。上面来看下官网给进去的demo:

 `1package main`
 `2`
 `3import (`
 `4    "fmt"`
 `5    "sync"`
 `6)`
 `7`
 `8func main() {`
 `9    var once sync.Once`
`10    onceBody := func() {`
`11        fmt.Println("Only once")`
`12    }`
`13    done := make(chan bool)`
`14    for i := 0; i < 10; i++ {`
`15        go func() {`
`16            once.Do(onceBody)`
`17            done <- true`
`18        }()`
`19    }`
`20    for i := 0; i < 10; i++ {`
`21        <-done`
`22    }`
`23}`
`24// 后果只打印一次:only once`

源码剖析

sync.Once的源代码还很少的,间接在代码外面作剖析

 `1package sync`
 `2`
 `3import (`
 `4    "sync/atomic"`
 `5)`
 `6`
 `7type Once struct {`
 `8    done uint32 // 初始值为0示意还未执行过,1示意曾经执行过`
 `9    m    Mutex  // m为互斥锁`
`10}`
`11`
`12func (o *Once) Do(f func()) {`
`13    // 判断done是否为0.若为0,示意未执行过,调用doSlow办法`
`14    if atomic.LoadUint32(&o.done) == 0 {`
`15        o.doSlow(f)`
`16    }`
`17}`
`18`
`19func (o *Once) doSlow(f func()) {`
`20    // 互斥锁加锁解锁`
`21    o.m.Lock()`
`22    defer o.m.Unlock()`
`23    // 加锁判断done是否为0`
`24    if o.done == 0 {`
`25        // 执行完f()函数后,将done值设置为1`
`26        defer atomic.StoreUint32(&o.done, 1)`
`27        f()`
`28    }`
`29}`

Do办法流程图:

疑难1:为什么在do办法的时候没有用到cas原子判断?

在do办法源码正文中的时候有这么一段话,说如果是应用 atomic.CompareAndSwapUint32(&o.done, 0, 1)此办法不会保障实现f函数调用实现。为什么会这么说,同时进行两次调用,cas的获胜者将调用f函数,失败者将立刻返回,而无需期待第一个对f的调用实现。此时f函数还在执行过程中,你就曾经回复了once.Do胜利了,此时全局变量还没有创立进去,行为是无奈定义的。那么怎么解决呢?有以下两个思路:

1)热门路:用原子读done的值,保障竞态条件正确;

2)加锁:既然不能用cas原子操作,那就用加锁的形式来保障原子性,如果done==0,那么走慢门路,先加锁,而后在执行f函数,最初将done设置为1,并解锁。

重点千万不要把同一sync.Once用在嵌套构造中,这样非常容易造成死锁!

`1func once() {`
`2    once := &sync.Once{}`
`3    once.Do(func() {`
`4        once.Do(func() {`
`5            fmt.Println("test nestedDo")`
`6        })`
`7    })`
`8    //输入:fatal error: all goroutines are asleep - deadlock!`
`9}`

sync.Once实现单例模式

单例模式能够说是设计模式外面最简略的了,Go能够借助sync.Once来实现,代码如下:

 `1package main`
 `2`
 `3import (`
 `4    "fmt"`
 `5    "sync"`
 `6)`
 `7`
 `8type Singleton struct{}`
 `9`
`10var (`
`11    singleton *Singleton`
`12    once      sync.Once`
`13)`
`14`
`15func GetSingleton() *Singleton {`
`16    once.Do(func() {`
`17        fmt.Println("Create Obj")`
`18        singleton = new(Singleton)`
`19    })`
`20    return singleton`
`21}`
`22`
`23func main() {`
`24    var wg sync.WaitGroup`
`25    for i := 0; i < 5; i++ {`
`26        wg.Add(1)`
`27        go func() {`
`28            obj := GetSingleton()`
`29            fmt.Printf("%p\n", obj)`
`30            wg.Done()`
`31        }()`
`32    }`
`33    wg.Wait()`
`34    // 只打印一次 Create Obj`
`35}`

扫码关注

获取更多干货内容

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理