go单例靠sync.once+指针变量实现,确保全局唯一且并发安全:包级指针变量存储实例,once.do保证初始化仅一次;需返回错误、避免init()、注意实例内部线程安全。
Go 里没有“构造函数”,单例靠 sync.Once + 指针变量控制初始化
Go 不支持类和私有构造函数,所以不能像 Java 那样靠访问控制实现单例。核心思路是:用一个全局指针变量存实例,配合 sync.Once 保证只初始化一次。关键不是“禁止 new”,而是“只允许一次赋值”。
常见错误是只用 if instance == nil 判断——在并发场景下会创建多个实例。
-
sync.Once.Do()内部带互斥锁,且能确保即使多个 goroutine 同时调用,也仅执行一次传入的函数 - 实例变量必须是指针类型(如
*Config),否则赋值时是副本,外部拿不到初始化结果 - 初始化函数里不要做耗时操作(比如读文件、连数据库),否则会阻塞所有后续调用;可考虑异步加载或预热
标准线程安全单例写法(带延迟初始化)
这是最常用、最稳妥的写法,兼顾懒加载和并发安全:
var (
instance *DBClient
once sync.Once
)
<p>func GetDBClient() <em>DBClient {
once.Do(func() {
instance = &DBClient{ /</em> 初始化逻辑 */ }
})
return instance
}
注意:once 和 instance 必须是包级变量(不能放在函数内),否则每次调用 GetDBClient 都会新建一组变量,失去单例意义。