Go语言sync包之Once | 青训营笔记

95 阅读1分钟

2. sync.Once原理

sync.Once 是 Go 语言提供的内置并发安全的单次执行的对象,开箱即用。sync.Once 的原理非常简单,只有一个 Do 方法,其原型如下:

func (o *Once) Do(f func()) {}

Do 方法接收一个函数作为参数,当第一次调用 Do 方法时,会执行传入的函数 f,并且会将 done 置为 true,当第二次调用 Do 方法时,会直接返回,不会再次执行函数 f。

sync.Once 的内部结构如下:

type Once struct {
    m    Mutex
    done uint32
}

sync.Once 内部有一个互斥锁 m,同时有一个 done 标识,用于标识函数是否已经执行。

sync.Once 的 Do 方法的实现如下:

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()
    }
}

sync.Once 的 Do 方法的实现非常简单,只有两个步骤:

  1. 判断 done 是否为 1,如果为 1,则直接返回
  2. 如果 done 不为 1,则加锁,然后再次判断 done 是否为 1,如果为 1,则直接返回,如果不为 1,则执行函数 f,并将 done 置为 1

3. sync.Once的使用

sync.Once 的使用非常简单,直接声明变量即可,不需要初始化。sync.Once 的声明如下:

var once = sync.Once{}

sync.Once 的操作如下:

once.Do(func() {
    // 需要执行的函数
})

4. sync.Once的注意事项

  • sync.Once 不能在创建后再进行复制操作。
  • sync.Once 与其他结构不同,它不是一个结构体,因此我们在声明的时候只需要声明变量即可,不需要初始化。