
Syncing code can be a real pain, but in Golang, there's a solution that makes it a breeze: sync.Once. This nifty tool allows you to run code only once, even in concurrent environments.
It's a simple yet powerful concept, and understanding how it works is crucial for efficient coding. You can use sync.Once to initialize variables, load data, or perform any other operation that needs to be executed only once.
Here's the key benefit of sync.Once: it ensures that code is executed only once, even when called multiple times concurrently. This is particularly useful in situations where you need to set up a resource or perform an initialization task.
By using sync.Once, you can write more robust and reliable code that handles concurrent access with ease.
For more insights, see: Golang Source
What Is Once?
sync.Once is a synchronization primitive in the Go language's sync package. Its core function is to guarantee that a certain operation is executed only once during the program's lifecycle, regardless of how many goroutines call it simultaneously.
The official definition is concise and powerful: Once is an object that ensures a certain operation is performed only once. Once the Once object is used for the first time, it must not be copied.
sync.Once is particularly useful in scenarios where you want to perform a certain initialization or setup operation only once, regardless of how many times that operation is requested. It's commonly used in scenarios like this:
- Lazy Initialization: When you want to initialize a resource or perform a setup operation only when it is first needed, and you want to avoid the overhead of repeated initialization.
- Package-Level Initialization: In package-level variables or initialization functions, where you want to ensure that certain setup code is executed only once when the package is used.
Here are the key features of sync.Once:
- Guarantees that a certain operation is executed only once
- Ensures memory consistency after the operation is finished
- Prevents the Once object from being copied after it's used
- Returns the results of the operation, even if called concurrently
sync.Once is a powerful tool for ensuring that certain operations are executed only once in your Go program. With its help, you can avoid the overhead of repeated initialization and ensure that your code is safe and efficient.
New Features of Go 1.21
Go 1.21 introduced three new features to the sync.Once package: OnceFunc, OnceValue, and OnceValues. These functions encapsulate a common usage of Once, designed for deferred initialization of a value upon its initial use.
OnceFunc returns a function that invokes f only once, and the returned function may be called concurrently. If f panics, the returned function will panic with the same value on every call.
Signal wakes one goroutine waiting on c, if there is any. This can be useful in certain synchronization scenarios.
A Pool is a set of temporary objects that may be individually saved and retrieved. This can be useful for managing resources in a concurrent environment.
OnceValue returns a function that invokes f only once and returns the value returned by f. The returned function may be called concurrently, making it useful for expensive computations that only need to be performed once.
For example, this can be used to perform an "expensive" computation just once, even when used concurrently. This can improve performance in certain situations.
Broaden your view: Golang Func Type
How It Works?
Sync.Once in Golang is a library that allows you to easily implement distributed locks and leader election in your applications.
It uses a leader election algorithm to determine which node is the leader, and then uses a distributed lock to ensure that only the leader can make changes to the system.
The leader election algorithm is based on the Raft consensus algorithm, which is designed to be fault-tolerant and highly available.
The distributed lock is implemented using a simple token-based system, where the leader holds a token and other nodes must wait for the leader to release it before making changes.
How It Works?
Here's how it all works together: the system uses a combination of sensors and algorithms to detect and respond to various stimuli.
The sensors are designed to pick up on subtle changes in the environment, such as temperature fluctuations or changes in air pressure. This allows the system to adapt and respond accordingly.
The algorithms used in the system are based on complex mathematical formulas that help to analyze the data collected by the sensors. This helps to ensure that the system responds in a logical and predictable way.

The system can be programmed to respond to a wide range of stimuli, from simple changes in temperature to more complex patterns of behavior. This makes it a versatile tool that can be used in a variety of different contexts.
One of the key benefits of the system is its ability to learn and adapt over time. This allows it to become more effective and efficient as it encounters new situations and stimuli.
The system is also designed to be highly scalable, meaning that it can be easily integrated into larger systems and networks. This makes it a valuable tool for organizations and businesses that need to manage complex systems and processes.
Func (*Once) Do
The (*Once) Do function is a game-changer for initialization that must be run exactly once.
It calls the function f if and only if Do is being called for the first time for this instance of Once.

Given multiple calls to once.Do(f), only the first call will invoke f, even if f has a different value in each invocation.
A new instance of Once is required for each function to execute.
Do is intended for initialization that must be run exactly once.
Since f is niladic, it may be necessary to use a function literal to capture the arguments to a function to be invoked by Do.
Because no call to Do returns until the one call to f returns, if f causes Do to be called, it will deadlock.
If f panics, Do considers it to have returned; future calls of Do return without calling f.
Source Code Analysis
The source code of sync.Once is extremely concise, with only 78 lines, including comments. This brevity is a testament to the simplicity of its design.
The source code is surprisingly effective, containing an exquisite design that showcases the power of minimalism.
You might like: Golang Design
Typical Usage Scenarios
In Go, the sync.once package is a powerful tool for ensuring that resources are initialized only once. This is particularly useful for database connection pools, which can be expensive to create.
To ensure that database connection pools are initialized only once, you can use the sync.once pattern, as mentioned in the documentation. This pattern is also useful for configuration loading, where you want to load a configuration file only once.
Lazy loading is another scenario where sync.once shines. By loading resources only when needed, you can save memory and improve performance. This is especially important in applications with a large number of resources.
Here are some typical usage scenarios for sync.once:
- Singleton pattern: Ensure that database connection pools, configuration loading, etc., are initialized only once
- Lazy loading: Load resources only when needed, and only once
- Concurrent safe initialization: Safe initialization in a multi-goroutine environment
In a multi-goroutine environment, sync.once helps ensure that resources are initialized safely and only once. This prevents concurrent initialization issues and ensures that your application runs smoothly.
Performance and Precautions
sync.Once has excellent performance, with the first call taking about 50-100ns due to the mutex lock, and subsequent calls having almost zero overhead at around 1-2ns.
In high-concurrency scenarios, sync.Once can significantly reduce performance loss compared to other synchronization methods. This is because it's designed to handle a large number of concurrent calls efficiently.
Here are some key performance considerations to keep in mind:
To ensure you get the most out of sync.Once, be aware of some important precautions. Not copying the once value after its first use can lead to undefined behavior, and recursive calls to sync.Once can cause a deadlock.
Performance Considerations
Sync.Once has excellent performance, with an overhead of around 50-100ns for the first call due to the mutex lock.
In high-concurrency scenarios, sync.Once can significantly reduce performance loss compared to other synchronization methods.
The first call to sync.Once has a significant delay, but subsequent calls are almost instantaneous, taking only about 1-2ns.
This is because subsequent calls only require an atomic loading operation, which is much faster than the initial mutex lock.
Here's a breakdown of the performance differences:
Precautions

To ensure optimal performance and avoid potential issues, be aware of the following precautions.
Not copying an object once it contains a noCopy field is crucial, as it can lead to undefined behavior if you try to copy it after the first use.
Avoiding recursive calls is also essential, particularly when using once.Do(f), as it can cause a deadlock if f calls once.Do again.
If a panic occurs in a function that's being executed by once.Do, it will be treated as if the function has been executed, and subsequent calls will no longer run the function.
OnceFunc and Functionality
OnceFunc returns a function that invokes f only once. The returned function may be called concurrently.
If f panics, the returned function will panic with the same value on every call. This is a key difference between OnceFunc and the native once.Do, which will only panic on the first time.
OnceFunc is concurrent safe, making it a reliable choice for multi-goroutine environments.
Here are some typical usage scenarios for OnceFunc:
- Singleton pattern: Ensure that database connection pools, configuration loading, etc., are initialized only once
- Lazy loading: Load resources only when needed, and only once
- Concurrent safe initialization: Safe initialization in a multi-goroutine environment
Quick Start
Getting started with sync.Once in Go is a breeze. It has only one core method called Do, which is extremely simple to use.
The running result of Do is always the same, regardless of how many times it's called in a single goroutine.
Featured Images: pexels.com


