Skip to content

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 #单例模式 #初始化 #并发安全难度等级: ⭐⭐⭐ 面试频率: 高频

正在精进