0
0
GoHow-ToBeginner · 3 min read

How to Use sync.Once in Go for One-Time Execution

In Go, sync.Once ensures a function runs only once, even if called from multiple goroutines. Use its Do method with the function you want to execute exactly one time.
📐

Syntax

The sync.Once type has a method Do(f func()) that runs the function f only once. Even if multiple goroutines call Do, f executes just one time.

  • var once sync.Once: declares a Once variable.
  • once.Do(func()): runs the function once.
go
var once sync.Once

once.Do(func() {
    // code to run once
})
💻

Example

This example shows how sync.Once runs initialization code only once, even when called from multiple goroutines.

go
package main

import (
	"fmt"
	"sync"
)

var once sync.Once

func initialize() {
	fmt.Println("Initialization done")
}

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done()
	once.Do(initialize)
	fmt.Printf("Worker %d done\n", id)
}

func main() {
	var wg sync.WaitGroup
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go worker(i, &wg)
	}
	wg.Wait()
}
Output
Initialization done Worker 1 done Worker 2 done Worker 3 done
⚠️

Common Pitfalls

One common mistake is calling Do with a function that has side effects or depends on external state that changes, expecting it to run multiple times. sync.Once runs the function only once, so subsequent calls do nothing.

Another pitfall is trying to reuse the same sync.Once variable to run different functions multiple times; it only allows one function execution ever.

go
package main

import (
	"fmt"
	"sync"
)

func main() {
	var once sync.Once

	// Wrong: trying to run two different functions with the same Once
	once.Do(func() { fmt.Println("First run") })
	once.Do(func() { fmt.Println("Second run") }) // This will NOT run

	// Correct: use separate Once variables for different one-time actions
	var once2 sync.Once
	once2.Do(func() { fmt.Println("Second run") })
}
Output
First run Second run
📊

Quick Reference

  • sync.Once: type to ensure one-time execution.
  • Do(func()): runs the function once, ignores later calls.
  • Safe for concurrent use by multiple goroutines.
  • Use separate sync.Once variables for different one-time actions.

Key Takeaways

Use sync.Once.Do to run a function exactly once, even with many goroutines.
sync.Once is safe for concurrent use without extra locking.
Do not expect the function to run more than once per sync.Once variable.
Use separate sync.Once variables for different one-time initializations.
sync.Once helps avoid race conditions in one-time setup code.