Golang / Golang Internals and Memory Management Interview Questions
How does sync.Once work and what are its use cases?
sync.Once guarantees that a function is executed exactly once, regardless of how many goroutines call it concurrently. It is the idiomatic Go way to implement lazy initialisation and singletons without explicit locking in user code.
var ( instance *Database once sync.Once ) func GetDB() *Database { once.Do(func() { // This function runs exactly once across all goroutines instance = &Database{ conn: openConnection(config), } }) return instance // safe to return â once.Do blocks until init completes } // All concurrent callers either: // - Execute the function themselves (first caller) // - Block until the function completes and then return (all others) // ERROR HANDLING in sync.Once â no built-in mechanism // Pattern: store the error alongside the result type singleton struct { db *Database err error } var s singleton var initOnce sync.Once func getDB() (*Database, error) { initOnce.Do(func() { s.db, s.err = openDB() }) return s.db, s.err } // sync.OnceFunc (Go 1.21) â wraps a function so it runs at most once initDB := sync.OnceFunc(func() { /* initialise */ }) initDB() // safe to call from multiple goroutines initDB() // no-op â function already ran // sync.OnceValue / sync.OnceValues (Go 1.21) â return a value getConfig := sync.OnceValue(func() *Config { return loadConfig() }) cfg := getConfig() // computed once, cached
sync.Once has no reset mechanism — once the function has run, there is no way to make it run again. This is by design. If you need resettable one-time execution, use a mutex-protected boolean flag instead. Go 1.21 added sync.OnceFunc, sync.OnceValue, and sync.OnceValues as ergonomic wrappers.
More Related questions...