Go by Example

Atomic Counters

Use typed sync/atomic operations for independent counter updates and reads across goroutines.

A data race occurs when concurrent goroutines access the same memory location without synchronization and at least one access is a write. A plain counter increment can lose updates because it reads, changes, and writes the value in separate steps. The sync/atomic package provides operations that update a counter as one indivisible operation.

atomic.Int64 is a typed counter available since Go 1.19. Add increments it safely, and Load reads its current value.

package main
 
import (
    "fmt"
    "sync"
    "sync/atomic"
)
 
func main() {
    var counter atomic.Int64
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Add(1)
        }()
    }
    wg.Wait()
    fmt.Println(counter.Load())
}

In production

Atomic operations work well for independent counters and flags. They do not make a group of fields change together as one unit. Use a mutex when correctness depends on keeping multiple values consistent, and do not copy a typed atomic after its first use.

Enjoyed this? Get more software craft delivered to your inbox.