MST

星途 面试题库

面试题:Go语言中sync.Once实现懒汉式初始化的原理

请详细阐述在Go语言里,sync.Once是如何实现懒汉式初始化模式的,包括sync.Once结构体的主要字段及其作用,以及Do方法的具体执行逻辑。
23.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

sync.Once结构体主要字段及其作用

sync.Once 结构体在Go语言中的定义如下:

type Once struct {
    done uint32
    m    Mutex
}
  1. done (uint32)
    • 用于标记初始化是否已经完成。它是一个32位无符号整数,当它的值为0时,表示初始化尚未进行;非0值表示初始化已经完成。通过使用原子操作对其进行读写,以确保在多线程环境下的正确性。
  2. m (Mutex)
    • 互斥锁,用于保护对 done 字段的读写操作,以及在初始化未完成时,保护初始化函数的执行。当有多个 goroutine 同时尝试初始化时,通过互斥锁来保证只有一个 goroutine 能够进入初始化逻辑,避免重复初始化。

Do方法的具体执行逻辑

Do 方法的定义如下:

func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 0 {
       o.doSlow(f)
    }
}

func (o *Once) doSlow(f func()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
       defer atomic.StoreUint32(&o.done, 1)
       f()
    }
}
  1. 快速路径检查
    • Do 方法首先使用 atomic.LoadUint32 原子操作读取 done 字段的值。如果 done 不为0,说明初始化已经完成,直接返回,不再执行初始化函数 f。这是一个快速路径,避免了在已经初始化完成的情况下获取锁和重复执行初始化函数的开销。
  2. 慢速路径执行
    • 如果 done 为0,说明初始化尚未完成,调用 doSlow 方法。
    • doSlow 方法中,首先获取互斥锁 o.m,这样可以确保只有一个 goroutine 能够进入初始化逻辑。
    • 再次检查 done 是否为0,这是因为在获取锁的过程中,可能已经有其他 goroutine 完成了初始化。如果 done 仍然为0,则执行初始化函数 f
    • 在执行 f 之前,使用 defer 语句确保在 f 执行完毕后,通过 atomic.StoreUint32 原子操作将 done 设置为1,表示初始化已经完成。然后释放互斥锁。

通过这种方式,sync.Once 实现了懒汉式初始化模式,在多线程环境下保证初始化函数只被执行一次。