go sync.Once 源码分析

sync.Once 是 Go 语言标准库中的一个同步原语,用于确保某个操作或函数在并发环境下只执行一次。它通常用于以下场景:

  1. 单例模式:确保全局只有一个实例对象,避免重复创建资源
  2. 延迟初始化:在程序运行过程中,当真正需要某个资源时才进行初始化
  3. 执行性一次的操作:例如加载配置文件、初始化日志系统等

使用方法

sync.Once 提供了一个方法 Do,接受一个函数作为参数。无论 Do 被调用多少次,传入的函数只会执行一次。例如:

var once sync.Once
var config *Config

func ReadConfig() *Config {
    once.Do(func() {
        config = &Config{Server: "example.com", Port: 8080}
    })
    return config
}

在这个例子中,config 的初始化只会在第一次调用 ReadConfig 时执行,后续调用直接返回已初始化的 config

特点

  • 线程安全:即使在多个协程同时调用 Do 方法,也能保证函数只执行一次。
  • 不可重用:一旦 sync.Once 执行完成,它将标记为“已完成”,后续调用不会重新执行。
  • 异常处理:如果 Do 中的函数发生 panic,sync.Once 会将其视为“已完成”,后续调用不会重新执行。

源码分析

sync.Once 的实现基于双重锁检查机制实现的。它通过 atomic.LoadUint32atomic.StoreUint32 来检查和标记操作是否已完成。这种设计既保证了性能,又确保了线程安全。

package sync

import (
	"sync/atomic"
)

type Once struct {
	done uint32
	m    Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {
		// Outlined slow-path to allow inlining of the fast-path.
		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() 触发 panic 也会标记为 “已完成”,后续调用不会重新执行
		f()
	}
}

双重锁检查机制

双重锁检查机制(Double-Checked Locking,DCL)是一种用于多线程环境中实现延迟初始化单例模式的设计模式,其核心思想是通过两次检查来减少同步锁的开销,同时保证线程安全。

工作原理

  1. 第一次检查:在进入同步代码块之前,先检查实例是否已经被初始化。如果实例已经存在,则直接返回实例,避免进入同步块。
  2. 加锁:如果实例尚未初始化,线程将尝试获取锁,以确保只有一个线程可以进入初始化代码块。
  3. 第二次检查:在获取锁后,再次检查实例是否已经被初始化。这是为了确保在当前线程等待锁的过程中,其他线程没有初始化实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值