๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

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?