Go Atomic Operations
Learn Go through interactive, bite-sized lessons. Build scalable applications with modern concurrency.
Start Go Journey →Atomic operations in Go provide a way to perform thread-safe operations on shared variables without using locks. These operations are essential for concurrent programming and ensuring data integrity in multi-threaded environments.
What are Atomic Operations?
Atomic operations are indivisible and uninterruptible actions that complete in a single step from the perspective of other Go Goroutines. They are crucial for managing shared resources in concurrent programs.
The sync/atomic Package
Go provides atomic operations through the sync/atomic package. This package offers functions for performing atomic operations on integers and pointers.
Common Atomic Functions
atomic.AddInt64(): Atomically adds a value to an int64atomic.LoadInt64(): Atomically loads an int64 valueatomic.StoreInt64(): Atomically stores an int64 valueatomic.CompareAndSwapInt64(): Atomically compares and swaps an int64 value
Example: Atomic Counter
Here's a simple example of using atomic operations to implement a thread-safe counter:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
atomic.AddInt64(&counter, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println("Counter:", atomic.LoadInt64(&counter))
}
In this example, we use atomic.AddInt64() to increment the counter safely across multiple goroutines. The atomic.LoadInt64() function is used to read the final value.
Compare and Swap
The Compare and Swap (CAS) operation is a powerful atomic operation that allows you to update a value only if it matches an expected value. Here's an example:
func incrementIfEven(addr *int64) bool {
for {
old := atomic.LoadInt64(addr)
if old%2 != 0 {
return false
}
if atomic.CompareAndSwapInt64(addr, old, old+1) {
return true
}
}
}
This function increments a value only if it's even, using atomic.CompareAndSwapInt64() to ensure thread-safety.
Best Practices
- Use atomic operations for simple shared state management
- For more complex scenarios, consider using Go Mutexes or channels
- Avoid mixing atomic operations with other synchronization methods on the same variables
- Remember that atomic operations are low-level primitives; higher-level constructs might be more appropriate for complex use cases
Conclusion
Atomic operations in Go provide a powerful tool for managing shared state in concurrent programs. They offer better performance than mutexes for simple operations but require careful use to ensure correctness. As you delve deeper into Go concurrency, understanding atomic operations becomes crucial for writing efficient and safe concurrent code.