在 Go 语言中,单例模式的实现与线程安全(Go 中称为 协程安全 或 并发安全)可以通过多种方式实现。以下是几种常见的方法:
1. 使用 sync.Once(推荐)
sync.Once 是 Go 标准库中专门设计用来确保某个操作只执行一次的机制,非常适合实现单例模式。
package singleton
import "sync"
type Singleton struct {
    // 单例对象的字段
}
var (
    instance *Singleton
    once     sync.Once
)
func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
        // 初始化单例对象的字段
    })
    return instance
}
原理:
- once.Do保证内部的初始化函数只会执行一次,无论有多少协程同时调用- GetInstance。
- 线程安全,无需手动管理锁。
优点:
- 简洁高效,无锁竞争。
- 延迟初始化(懒加载),只有在首次调用时创建实例。
2. 使用互斥锁(sync.Mutex)
通过互斥锁确保初始化过程线程安全:
package singleton
import "sync"
type Singleton struct {
    // 单例对象的字段
}
var (
    instance *Singleton
    mu       sync.Mutex
)
func GetInstance() *Singleton {
    if instance == nil { // 第一次检查,避免不必要的锁竞争
        mu.Lock()
        defer mu.Unlock()
        if instance == nil { // 第二次检查,确保实例未被其他协程创建
            instance = &Singleton{}
            // 初始化单例对象的字段
        }
    }
    return instance
}
原理:
- 双重检查锁定(Double-Checked Locking)减少锁的竞争。
- 第一次检查避免每次调用都加锁,第二次检查确保实例唯一。
缺点:
- 代码稍复杂,需要手动管理锁。
3. 包级变量直接初始化
如果单例对象无需延迟初始化,可以直接在包初始化时创建:
package singleton
type Singleton struct {
    // 单例对象的字段
}
var instance = &Singleton{
    // 初始化字段
}
func GetInstance() *Singleton {
    return instance
}
原理:
- Go 的包初始化是线程安全的,instance会在程序启动时自动创建。
- 适合简单场景,但无法延迟初始化。
缺点:
- 如果初始化耗时,可能影响程序启动速度。
4. 利用 init 函数
在 init 函数中初始化单例:
package singleton
type Singleton struct {
    // 单例对象的字段
}
var instance *Singleton
func init() {
    instance = &Singleton{}
    // 初始化字段
}
func GetInstance() *Singleton {
    return instance
}
原理:
- init函数在包被导入时自动执行,且只会执行一次。
- 线程安全,但无法延迟初始化。
5. 避免单例模式(依赖注入)
在 Go 中,单例模式有时被认为是反模式。更好的做法是通过 依赖注入 或 全局配置 来管理共享资源:
// 将单例作为参数传递,而非全局变量
type Singleton struct {
    // 字段
}
func NewSingleton() *Singleton {
    return &Singleton{}
}
// 使用依赖注入
func Process(s *Singleton) {
    // 使用单例对象
}
优点:
- 避免全局状态,提升代码可测试性和可维护性。
总结
- 推荐方式:使用 sync.Once,简洁高效且线程安全。
- 延迟初始化:优先选择 sync.Once或双重检查锁定。
- 简单场景:直接使用包级变量或 init函数。
- 最佳实践:尽量避免全局单例,优先通过依赖注入管理共享资源。
示例(完整版):
package singleton
import (
    "sync"
)
type singleton struct {
    // 单例对象的字段
    Value int
}
var (
    instance *singleton
    once     sync.Once
)
func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{
            Value: 42, // 初始化字段
        }
    })
    return instance
}
