Once
sync.Once
是 Go 语言标准库 sync
包中的一个类型,用于确保某个操作在并发环境下只执行一次。它通常用于初始化资源或执行一次性设置操作,避免重复执行带来的问题。
核心功能
sync.Once
的核心方法是 Do
,其签名如下:
1func (o *Once) Do(f func())
Do
方法接收一个函数 f
作为参数,并保证该函数只执行一次,即使多次调用 Do
方法也是如此。
使用场景
- 单例模式:确保全局唯一实例的创建。
- 初始化操作:如数据库连接、配置加载等,确保只执行一次。
- 资源释放:确保资源只释放一次,避免重复释放。
内部实现
sync.Once
内部通过一个 uint32
类型的标志位和一个互斥锁 m
来实现。Do
方法首先检查标志位,如果为 0,则执行传入的函数并将标志位置为 1,确保函数只执行一次。
1package sync
2
3import (
4 "sync/atomic"
5)
6
7type Once struct {
8 done atomic.Uint32
9 m Mutex
10}
11
12func (o *Once) Do(f func()) {
13 if o.done.Load() == 0 {
14 o.doSlow(f)
15 }
16}
17
18func (o *Once) doSlow(f func()) {
19 o.m.Lock()
20 defer o.m.Unlock()
21 if o.done.Load() == 0 {
22 defer o.done.Store(1)
23 f()
24 }
25}
注意事项
sync.Once
是不可重用的,即一旦 Do
方法执行了传入的函数,后续调用 Do
方法将不会再次执行该函数。可以额外调整,使得当传入函数返回错误时,再次调用 Do
方法可以再次执行传入的函数。
1package zonce
2
3import (
4 "sync"
5 "sync/atomic"
6)
7
8type Once struct {
9 done uint32
10 sync.Mutex
11}
12// 传入的函数f有返回值error,如果初始化失败,需要返回失败的error
13// Do方法会把这个error返回给调用者
14func (o *Once) Do(f func() error) error {
15 if atomic.LoadUint32(&o.done) == 1 { //fast path
16 return nil
17 }
18 return o.doSlow(f)
19}
20
21func (o *Once) doSlow(f func() error) error {
22 o.Lock()
23 defer o.Unlock()
24 var err error
25 if o.done == 0 {
26 if err := f(); err == nil {
27 // 初始化成功才将标记置为已初始化
28 atomic.StoreUint32(&o.done, 1)
29 }s
30 }
31 return err
32}