How to use atomic operations

Use the `sync/atomic` package when you need lock-free, thread-safe updates to simple variables like integers, pointers, or booleans, avoiding the overhead of `sync.Mutex` for high-frequency counters or flags.

Use the sync/atomic package when you need lock-free, thread-safe updates to simple variables like integers, pointers, or booleans, avoiding the overhead of sync.Mutex for high-frequency counters or flags. These functions guarantee that read-modify-write sequences happen indivisibly, preventing race conditions without blocking goroutines.

For standard integer operations, use AddInt64, SwapInt64, or CompareAndSwapInt64 (CAS). CAS is particularly powerful for implementing complex lock-free algorithms where you need to update a value only if it hasn't changed since you last read it.

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var counter int64

	// Increment safely without a mutex
	atomic.AddInt64(&counter, 1)

	// Swap the value and get the old one (useful for state machines)
	oldVal := atomic.SwapInt64(&counter, 100)
	fmt.Printf("Old: %d, New: %d\n", oldVal, atomic.LoadInt64(&counter))

	// Compare-And-Swap: only update if current value is 100
	success := atomic.CompareAndSwapInt64(&counter, 100, 200)
	fmt.Printf("CAS Success: %v, Value: %d\n", success, atomic.LoadInt64(&counter))
}

For pointers, use LoadPointer and StorePointer to safely pass references between goroutines, or SwapPointer to atomically replace them. Be careful with 64-bit alignment on 32-bit systems; ensure the target variable is aligned to 64 bits (usually by placing it as the first field in a struct) or use atomic.LoadInt64/StoreInt64 on a *int64 that is properly aligned.

package main

import (
	"fmt"
	"sync/atomic"
)

type Data struct {
	value int
}

func main() {
	var ptr *Data
	initial := &Data{value: 1}

	// Store pointer safely
	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&ptr)), (*unsafe.Pointer)(unsafe.Pointer(&initial)))

	// Load pointer safely
	loaded := (*Data)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&ptr))))
	fmt.Printf("Loaded value: %d\n", loaded.value)
}

Avoid using atomic for complex data structures or multi-step transactions; if you need to update multiple fields atomically, use a sync.Mutex or sync.RWMutex instead. Atomic operations are strictly for single-word operations. Also, remember that while atomic prevents data races, it does not enforce memory ordering beyond what the specific function guarantees, so use atomic.Load/Store carefully when coordinating complex state changes across goroutines.