Go Sync Once
A sync.Once value runs a function exactly one time. Every later Do call returns without running the function again.
Three calls, one run
Main calls Do three times on one sync.Once value. A counter inside the function shows how often it ran.
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
ran := 0
setup := func() {
ran++
}
once.Do(setup)
once.Do(setup)
once.Do(setup)
fmt.Println("Ran:", ran)
}Setup runs on the first call
The function passed to Do builds a config value. Main reads the value after Do returns, and the print shows the built text.
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
config := ""
build := func() {
config = "standard"
}
once.Do(build)
once.Do(build)
fmt.Println("Config:", config)
}Many callers, one run
Three goroutines call Do on the same sync.Once value. Main prints the run count after the join.
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
ran := 0
setup := func() {
ran++
}
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
once.Do(setup)
}()
}
wg.Wait()
fmt.Println("Ran:", ran)
}Example
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
settings := ""
built := 0
build := func() {
settings = "standard"
built++
}
results := make([]string, 3)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
once.Do(build)
results[id] = settings
}(i)
}
wg.Wait()
for i, r := range results {
fmt.Printf("Worker %d: %s\n", i, r)
}
fmt.Println("Built:", built)
}Key points
- Use Do to run a function through a sync.Once value.
- The first Do call runs the function.
- Later Do calls return without running the function again.
- A value built inside the function is ready to read after Do returns.
- Many goroutines can call Do on one sync.Once value at the same time.
- The function still runs exactly once.
In this example, three workers each call Do on one sync.Once value. The build function runs for the first caller and sets the settings text. Every worker then reads the same settings into its own slot. Main joins the workers, prints the slots in order, and shows the build ran once.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท Go Playground
Exercise
Three goroutines call Do on one sync.Once value with the same function. How many times does the function run?