
本文介绍在 go 项目中通过 init() 函数一次性初始化全局配置变量,并结合互斥锁保障并发安全,避免重复执行初始化逻辑,同时提供线程安全的访问接口。
本文介绍在 go 项目中通过 init() 函数一次性初始化全局配置变量,并结合互斥锁保障并发安全,避免重复执行初始化逻辑,同时提供线程安全的访问接口。
在 Go 应用中,常需在启动时加载一次配置(如数据库连接串、API 密钥、证书路径等),并在整个程序生命周期内被多个包安全复用。若直接将函数调用赋值给导出的全局变量(如 var ConfigMap = GetAppConfig()),会导致每次导入该包时都重新执行初始化——这不仅低效,还可能引发资源重复加载或竞态问题。
正确的做法是:分离变量声明与初始化时机,利用 Go 的 init() 函数确保仅在包加载时执行一次初始化,并配合同步原语保护并发访问。
以下是推荐实现方案:
// file: utils/config.go
package utils
import (
"sync"
)
// configMap 是未导出的私有变量,防止外部直接修改
var configMap map[string]string
// confMu 用于保护 configMap 的并发读写
var confMu = &sync.RWMutex{} // 使用 RWMutex 更高效:多读少写场景下允许多个 goroutine 并发读取
// init 在包首次被导入时自动执行,且仅执行一次
func init() {
configMap = GetAppConfig()
}
// Config 获取指定 key 的配置值(并发安全读取)
func Config(key string) string {
confMu.RLock()
defer confMu.RUnlock()
return configMap[key]
}
// ConfigMapCopy 返回配置的深拷贝(避免外部篡改原始数据)
func ConfigMapCopy() map[string]string {
confMu.RLock()
defer confMu.RUnlock()
copyMap := make(map[string]string, len(configMap))
for k, v := range configMap {
copyMap[k] = v
}
return copyMap
}
// GetAppConfig 实际的配置加载逻辑(示例)
func GetAppConfig() map[string]string {
// 此处可读取 YAML/JSON 文件、环境变量或远程配置中心
return map[string]string{
"db_host": "localhost",
"db_port": "5432",
"tls_enabled": "true",
}
}✅ 关键要点说明:
- init() 函数由 Go 运行时自动调用,保证在 main() 执行前、且仅执行一次;
- 使用 sync.RWMutex 而非普通 Mutex,使高频读取场景性能更优;
- 不导出 configMap 变量(首字母小写),强制用户通过 Config() 或 ConfigMapCopy() 访问,杜绝意外修改;
- ConfigMapCopy() 提供不可变副本,适用于需传递或缓存配置快照的场景;
- 若配置支持热更新,可在后续扩展 Reload() 方法(需加写锁并替换 configMap 引用)。
这样设计后,在任意包中只需导入 "your-project/utils",即可安全调用 utils.Config("db_host") —— 配置已在应用启动时完成初始化,无重复开销,亦无并发风险。

















