使用sync.Once可确保初始化代码在并发环境下仅执行一次。它通过Do方法实现线程安全的懒加载,适用于数据库连接池等需单次初始化的场景;与init()函数不同,sync.Once支持运行时延迟初始化,且天生应对并发,但需注意初始化函数panic会导致永久失败,且无法重试或重置。

在Golang中,要确保一段初始化代码无论在何种并发环境下都只执行一次,最地道且推荐的做法是使用标准库中的
sync.Once
解决方案
sync.Once
Do
Do
我们来看一个典型的例子,比如我们想确保一个数据库连接池只被初始化一次:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"sync"
"time"
)
// DBManager 模拟一个数据库连接管理器
type DBManager struct {
conn string
}
var (
once sync.Once
dbMgr *DBManager
)
// InitDBManager 模拟数据库连接的初始化过程
func InitDBManager() *DBManager {
// 模拟耗时操作
time.Sleep(time.Millisecond * 100)
fmt.Println("正在初始化数据库连接...")
dbMgr = &DBManager{conn: "PostgreSQL Connection Pool"}
return dbMgr
}
// GetDBManager 获取数据库管理器实例
func GetDBManager() *DBManager {
once.Do(func() {
dbMgr = InitDBManager()
})
return dbMgr
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("协程 %d 尝试获取DB管理器...\n", id)
mgr := GetDBManager()
fmt.Printf("协程 %d 获取到DB管理器: %s\n", id, mgr.conn)
}(i)
}
wg.Wait()
fmt.Println("所有协程完成。")
}在这个例子中,
InitDBManager
GetDBManager
once.Do(func() { dbMgr = InitDBManager() })InitDBManager
GetDBManager
sync.Once
sync.Once
在并发编程的世界里,资源初始化往往是个棘手的问题。如果没有
sync.Once
想象一下,你有一个全局配置对象或者一个日志句柄,它只需要被创建一次。如果多个goroutine几乎同时尝试创建它,会发生什么?
sync.Once
Do
sync.Mutex
isInitialized
sync.Once
sync.Once
虽然
sync.Once
一个最常被提及的“陷阱”是:如果Do
sync.Once
举个例子:
var brokenOnce sync.Once
var brokenResource string
func initBrokenResource() {
fmt.Println("尝试初始化一个会panic的资源...")
if true { // 模拟一个总是会panic的条件
panic("初始化失败:模拟一个严重错误")
}
brokenResource = "我本应该被初始化"
}
func GetBrokenResource() string {
brokenOnce.Do(initBrokenResource)
return brokenResource
}
func main() {
// 第一次调用,会panic
// defer func() {
// if r := recover(); r != nil {
// fmt.Println("捕获到panic:", r)
// }
// }()
// fmt.Println(GetBrokenResource()) // 这一行会panic
// 如果不捕获panic,程序会崩溃。
// 如果捕获了,那么第二次调用GetBrokenResource(),initBrokenResource()不会再执行
// brokenResource 仍然是空字符串
}为了避免这种情况,我的建议是:初始化函数内部应该自行处理所有可能的错误,而不是让它panic。 如果初始化失败,应该返回一个错误,并在
Do
另一个需要注意的点是,
sync.Once
Do
sync.Once
最后,虽然不是陷阱,但值得一提的是,
sync.Once
dbMgr
sync.Once
init()
sync.Once
init()
init()
init()
main
init()
init()
init()
sync.Once
sync.Once
Do
Do
sync.Once
Do
init()
核心区别总结:
init()
sync.Once
Do
init()
sync.Once
init()
sync.Once
适用场景选择:
init()
sync.Once
在我看来,
init()
sync.Once
以上就是Golang中如何使用sync.Once确保初始化代码只执行一次的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号