通常的单例模式是通过加锁完成,大概如下:

    mu.Lock()
    defer mu.Unlock()

    if instance == nil {
        instance = &singleton{}    
    }
    return instance

但go提供了一个sync的包,它的Once如果Do方法被调用多次,只会运行第一次,

package main

import (
"sync"
"fmt"
)

type singleton struct {
}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}

func main() {
    a := GetInstance()
    b := GetInstance()
    fmt.Println(a == b)
}

测试结果a等于b 那它是怎么实现的呢?

func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 1 {
        return
    }
    // Slow-path.
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

有两个重点:第一是它必须等待方法完成才释放锁,这样就保证第一个方法的执行,并且在执行的过程中不会被调用第二次 第二是通过atomic原子操作,去判断方法是否已经被执行,这样以后的方法调用直接return。 细心的可能会发现为啥上面的o.done没有使用atomic.LoadUint32(&o.done)呢?因为外层已经加过锁了,这里就没有必要加锁了

results matching ""

    No results matching ""