Atomics in Go
Atomics in Go are low-level synchronization primitives provided by the sync/atomic
package. They allow developers to perform operations on shared variables safely without using more complex synchronization mechanisms like locks.
Atomic operations ensure that a particular operation on a shared variable is performed as a single, indivisible action.
This eliminates race conditions without the need for locks, making atomic operations faster and more lightweight than traditional synchronization techniques.
Common Atomic Operations in Go
The sync/atomic
package provides functions for common atomic operations on integers, unsigned integers, and pointers.
Load
Reads the value of an atomic variable safely.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64 = 42
value := atomic.LoadInt64(&counter)
fmt.Println("Loaded value:", value)
}
Store
Writes a value to an atomic variable safely.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64
atomic.StoreInt64(&counter, 100)
fmt.Println("Stored value:", counter)
}
Add
Performs an atomic addition operation, returning the new value.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int64
atomic.AddInt64(&counter, 5)
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
atomic.AddInt64(&counter, int64(i))
wg.Done()
}()
}
wg.Wait()
fmt.Println("After addition:", counter)
}
Compare and Swap (CAS)
Atomically compares the current value of a variable to an expected value and swaps it with a new value if they match. CAS is a foundational operation for building higher-level synchronization primitives.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64 = 42
swapped := atomic.CompareAndSwapInt64(&counter, 42, 100)
fmt.Println("Swapped:", swapped, "New value:", counter) // Swapped: true New value: 100
swapped = atomic.CompareAndSwapInt64(&counter, 50, 200)
fmt.Println("Swapped:", swapped, "New value:", counter) // Swapped: false New value: 100
}
Swap
Atomically replaces the current value with a new one and returns the old value.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64 = 10
oldValue := atomic.SwapInt64(&counter, 20)
fmt.Println("Old value:", oldValue, "New value:", counter)
}
Atomic Operations on Pointers
In addition to integers, Go’s sync/atomic
package provides functions for atomic operations on pointers. These functions are useful for safely updating references in concurrent programs.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var pointer unsafe.Pointer
data := "Hello, Atomic!"
atomic.StorePointer(&pointer, unsafe.Pointer(&data))
loaded := atomic.LoadPointer(&pointer)
fmt.Println("Loaded value:", *(*string)(loaded))
}