Once 单次执行
sync.Once 确保函数只执行一次,是实现单例模式和初始化逻辑的重要工具。
Once 基础用法
基本使用
问题: sync.Once 的基本用法是什么?
回答: sync.Once 确保函数只执行一次,常用于单例模式和初始化逻辑。
点击查看完整代码示例
go
package main
import (
"fmt"
"sync"
)
var once sync.Once
var instance *Singleton
type Singleton struct {
data string
}
func GetInstance() *Singleton {
once.Do(func() {
fmt.Println("创建单例实例")
instance = &Singleton{data: "singleton"}
})
return instance
}
func basicOnceExample() {
fmt.Println("=== Once基础用法 ===")
// 多次调用,但初始化函数只执行一次
for i := 0; i < 5; i++ {
inst := GetInstance()
fmt.Printf("调用 %d: %p, data: %s\n", i+1, inst, inst.data)
}
}
func main() {
basicOnceExample()
}Once 实现原理
内部机制
问题: sync.Once 是如何实现的?
回答: sync.Once 使用原子操作和互斥锁实现双重检查模式,确保函数只执行一次:
查看详细实现原理和演示代码
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
go
// sync.Once 的简化实现
type Once struct {
done uint32
m sync.Mutex
}
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()
}
}
// 演示原理
func demonstrateOnceInternals() {
fmt.Println("\n=== Once内部机制演示 ===")
var myOnce sync.Once
var counter int
// 启动多个goroutine并发调用
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
myOnce.Do(func() {
counter++
fmt.Printf("Goroutine %d 执行初始化, counter=%d\n", id, counter)
})
fmt.Printf("Goroutine %d 完成\n", id)
}(i)
}
wg.Wait()
fmt.Printf("最终counter值: %d\n", counter)
}::: ::: ::: :::
高级应用场景
资源初始化
问题: 如何使用 Once 进行资源初始化?
回答: 使用 sync.Once 可以确保资源只初始化一次,常见于数据库连接、配置加载等场景:
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
go
import (
"database/sql"
"log"
)
type DatabaseManager struct {
db *sql.DB
once sync.Once
}
func (dm *DatabaseManager) GetDB() *sql.DB {
dm.once.Do(func() {
var err error
dm.db, err = sql.Open("mysql", "user:password@/dbname")
if err != nil {
log.Fatal("数据库连接失败:", err)
}
// 测试连接
if err = dm.db.Ping(); err != nil {
log.Fatal("数据库ping失败:", err)
}
fmt.Println("数据库连接已建立")
})
return dm.db
}
// 全局数据库管理器
var dbManager DatabaseManager
func GetDatabase() *sql.DB {
return dbManager.GetDB()
}
// 配置初始化示例
type Config struct {
Host string
Port int
Database string
}
var (
config *Config
configOnce sync.Once
)
func GetConfig() *Config {
configOnce.Do(func() {
// 模拟从文件或环境变量读取配置
config = &Config{
Host: "localhost",
Port: 3306,
Database: "myapp",
}
fmt.Println("配置已加载")
})
return config
}::: ::: :::
复杂初始化逻辑
问题: 如何处理复杂的初始化逻辑?
回答:
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
go
type ServiceManager struct {
logger *Logger
cache *Cache
db *Database
initOnce sync.Once
initError error
}
func (sm *ServiceManager) Initialize() error {
sm.initOnce.Do(func() {
// 复杂的初始化流程
if err := sm.initLogger(); err != nil {
sm.initError = fmt.Errorf("logger初始化失败: %w", err)
return
}
if err := sm.initCache(); err != nil {
sm.initError = fmt.Errorf("cache初始化失败: %w", err)
return
}
if err := sm.initDatabase(); err != nil {
sm.initError = fmt.Errorf("database初始化失败: %w", err)
return
}
fmt.Println("所有服务初始化完成")
})
return sm.initError
}
func (sm *ServiceManager) initLogger() error {
sm.logger = &Logger{level: "INFO"}
fmt.Println("Logger初始化完成")
return nil
}
func (sm *ServiceManager) initCache() error {
sm.cache = &Cache{size: 1000}
fmt.Println("Cache初始化完成")
return nil
}
func (sm *ServiceManager) initDatabase() error {
sm.db = &Database{host: "localhost"}
fmt.Println("Database初始化完成")
return nil
}
type Logger struct{ level string }
type Cache struct{ size int }
type Database struct{ host string }
// 使用示例
func complexInitializationExample() {
fmt.Println("\n=== 复杂初始化逻辑 ===")
sm := &ServiceManager{}
// 多个goroutine同时尝试初始化
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
if err := sm.Initialize(); err != nil {
fmt.Printf("Goroutine %d 初始化失败: %v\n", id, err)
} else {
fmt.Printf("Goroutine %d 初始化成功\n", id)
}
}(i)
}
wg.Wait()
}::: ::: :::
Once 的陷阱
常见错误
问题: 使用 sync.Once 时有哪些常见陷阱?
回答:
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
点击查看完整代码实现
go
// 陷阱1:panic导致Once永远无法重试
func panicTrap() {
fmt.Println("\n=== Once Panic陷阱 ===")
var once sync.Once
var result string
// 第一次调用会panic
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("捕获panic: %v\n", r)
}
}()
once.Do(func() {
panic("初始化失败")
})
}()
// 第二次调用不会执行,因为Once认为已经执行过了
once.Do(func() {
result = "成功初始化"
fmt.Println("第二次尝试初始化")
})
fmt.Printf("结果: %s\n", result) // 结果为空
}
// 陷阱2:错误的单例实现
var (
badInstance *Singleton
badOnce sync.Once
)
func GetBadInstance() *Singleton {
badOnce.Do(func() {
// 错误:在Do内部直接赋值给全局变量可能有竞态
badInstance = &Singleton{data: "bad"}
})
return badInstance
}
// 正确的实现
func GetGoodInstance() *Singleton {
badOnce.Do(func() {
// 正确:在Do内部创建,外部赋值
temp := &Singleton{data: "good"}
badInstance = temp
})
return badInstance
}
// 陷阱3:闭包变量捕获
func closureTrap() {
fmt.Println("\n=== Once闭包陷阱 ===")
var once sync.Once
config := "initial"
// 错误:闭包捕获的变量可能已经改变
config = "changed"
once.Do(func() {
fmt.Printf("闭包中的config: %s\n", config) // 输出"changed"
})
}
// 正确的处理方式
func correctClosureUsage() {
var once sync.Once
config := "initial"
// 将需要的值作为参数传递
initConfig := config
config = "changed"
once.Do(func() {
fmt.Printf("正确的config: %s\n", initConfig) // 输出"initial"
})
}::: ::: :::
Once 性能考虑
性能分析
问题: sync.Once 的性能特征是什么?
回答:
点击查看完整代码实现
点击查看完整代码实现
go
import (
"sync/atomic"
"testing"
)
// 性能测试:Once vs Mutex vs 原子操作
func BenchmarkOnce(b *testing.B) {
var once sync.Once
var result int
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
once.Do(func() {
result = 42
})
}
})
}
func BenchmarkMutex(b *testing.B) {
var mu sync.Mutex
var result int
var initialized bool
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
if !initialized {
result = 42
initialized = true
}
mu.Unlock()
}
})
}
func BenchmarkAtomic(b *testing.B) {
var result int64
var initialized int32
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if atomic.LoadInt32(&initialized) == 0 {
if atomic.CompareAndSwapInt32(&initialized, 0, 1) {
atomic.StoreInt64(&result, 42)
}
}
}
})
}
// 实际性能测试
func performanceComparison() {
fmt.Println("\n=== Once性能比较 ===")
// 测试场景:高并发读取已初始化的值
const goroutines = 1000
const iterations = 10000
// Once实现
var once sync.Once
var onceResult int
start := time.Now()
var wg sync.WaitGroup
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < iterations; j++ {
once.Do(func() {
onceResult = 42
})
_ = onceResult
}
}()
}
wg.Wait()
onceDuration := time.Since(start)
fmt.Printf("Once实现耗时: %v\n", onceDuration)
fmt.Printf("最终结果: %d\n", onceResult)
}
func main() {
basicOnceExample()
demonstrateOnceInternals()
complexInitializationExample()
panicTrap()
closureTrap()
correctClosureUsage()
performanceComparison()
}:::
技术标签: #sync.Once #单例模式 #初始化 #并发安全难度等级: ⭐⭐⭐ 面试频率: 高频
