sync.Once 基本概念
什么是 sync.Once
sync.OnceGoDoDosync.Once 的应用场景
sync.Once 主要用于以下场景:
sync.Oncesync.Once 应用实例
单例模式
sync.Oncepackage main
import (
"fmt"
"sync"
)
type Singleton struct{}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
s := GetInstance()
fmt.Printf("Singleton instance address: %pn", s)
}()
}
wg.Wait()
}GetInstanceonce.Do()instanceGetInstanceinstance = &Singleton{}s延迟初始化
sync.Oncepackage main
import (
"fmt"
"sync"
)
type Config struct {
config map[string]string
}
var (
config *Config
once sync.Once
)
func GetConfig() *Config {
once.Do(func() {
fmt.Println("init config...")
config = &Config{
config: map[string]string{
"c1": "v1",
"c2": "v2",
},
}
})
return config
}
func main() {
// 第一次需要获取配置信息,初始化 config
cfg := GetConfig()
fmt.Println("c1: ", cfg.config["c1"])
// 第二次需要,此时 config 已经被初始化过,无需再次初始化
cfg2 := GetConfig()
fmt.Println("c2: ", cfg2.config["c2"])
}Configsync.OnceGetConfigConfigConfigsync.Once 实现原理
type Once struct {
// 表示是否执行了操作
done uint32
// 互斥锁,确保多个协程访问时,只能一个协程执行操作
m Mutex
}
func (o *Once) Do(f func()) {
// 判断 done 的值,如果是 0,说明 f 还没有被执行过
if atomic.LoadUint32(&o.done) == 0 {
// 构建慢路径(slow-path),以允许对 Do 方法的快路径(fast-path)进行内联
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
// 加锁
o.m.Lock()
defer o.m.Unlock()
// 双重检查,避免 f 已被执行过
if o.done == 0 {
// 修改 done 的值
defer atomic.StoreUint32(&o.done, 1)
// 执行函数
f()
}
}sync.Oncedonemudoneuint32msync.OnceDodoSlowDofatomic.LoadUint32donefdoSlowdoSlowmfdonedoneffatomic.StoreUint32done为什么会封装一个 doSlow 方法
doSlowslow-pathDoDofast-pathinlined为什么会有双重检查(double check)的写法
doneatomic.LoadUint32donedonedoSlowdonefdonef通过双重检查,可以在大多数情况下避免锁竞争,提高性能。
加强的 sync.Once
sync.OnceDoerrorDosync.Oncepackage main
import (
"sync"
"sync/atomic"
)
type Once struct {
done uint32
m sync.Mutex
}
func (o *Once) Do(f func() error) error {
if atomic.LoadUint32(&o.done) == 0 {
return o.doSlow(f)
}
return nil
}
func (o *Once) doSlow(f func() error) error {
o.m.Lock()
defer o.m.Unlock()
var err error
if o.done == 0 {
err = f()
// 只有没有 error 的时候,才修改 done 的值
if err == nil {
atomic.StoreUint32(&o.done, 1)
}
}
return err
}Oncesync.OnceDoerrorerrordoneerrorsync.Once 的注意事项
死锁
sync.OncemDoDomutexfunc main() {
once := sync.Once{}
once.Do(func() {
once.Do(func() {
fmt.Println("init...")
})
})
}初始化失败
Doferrorsync.Oncesync.Onceonce