snippetgoModeratepending
Go sync.Once for thread-safe initialization
Viewed 0 times
Go 1.21+ for OnceValue/OnceValues
sync.Oncesingletonlazy-initthread-safeOnceValue
Problem
Need to initialize a resource exactly once, even when called from multiple goroutines concurrently.
Solution
Use sync.Once for thread-safe lazy initialization:
var (
instance *Database
once sync.Once
)
func GetDatabase() *Database {
once.Do(func() {
// This runs exactly once, even with concurrent calls
var err error
instance, err = NewDatabase(os.Getenv("DB_URL"))
if err != nil {
log.Fatal(err)
}
})
return instance
}
// sync.OnceValue (Go 1.21+) - returns a value
var getConfig = sync.OnceValue(func() *Config {
cfg, err := LoadConfig("config.yaml")
if err != nil {
log.Fatal(err)
}
return cfg
})
// Usage:
cfg := getConfig() // Loads once, returns cached after
// sync.OnceValues (Go 1.21+) - returns value and error
var getDB = sync.OnceValues(func() (*sql.DB, error) {
return sql.Open("postgres", os.Getenv("DB_URL"))
})
db, err := getDB()
if err != nil { ... }
var (
instance *Database
once sync.Once
)
func GetDatabase() *Database {
once.Do(func() {
// This runs exactly once, even with concurrent calls
var err error
instance, err = NewDatabase(os.Getenv("DB_URL"))
if err != nil {
log.Fatal(err)
}
})
return instance
}
// sync.OnceValue (Go 1.21+) - returns a value
var getConfig = sync.OnceValue(func() *Config {
cfg, err := LoadConfig("config.yaml")
if err != nil {
log.Fatal(err)
}
return cfg
})
// Usage:
cfg := getConfig() // Loads once, returns cached after
// sync.OnceValues (Go 1.21+) - returns value and error
var getDB = sync.OnceValues(func() (*sql.DB, error) {
return sql.Open("postgres", os.Getenv("DB_URL"))
})
db, err := getDB()
if err != nil { ... }
Why
sync.Once is simpler and safer than double-checked locking. OnceValue/OnceValues (Go 1.21+) add convenient return value support.
Revisions (0)
No revisions yet.