Sling Academy
Home/Golang/Using the `sync/Atomic` Package for Low-Level Synchronization

Using the `sync/Atomic` Package for Low-Level Synchronization

Last updated: November 27, 2024

When writing concurrent programs in Go, you'll often need to manipulate variables safely from multiple goroutines. The `sync/atomic` package in Go provides very efficient methods for synchronizing access to integers and pointers at a very low level without the need for mutexes.

Introduction to the `sync/atomic` Package

The `sync/atomic` package offers low-level atomic memory primitives for safe access and manipulation of shared data by multiple goroutines. This package is part of Go's standard library and provides atomic operations for common types such as integers and pointers.

Basic Operations

Some of the key functions provided by the `atomic` package include:

  • atomic.Load() and atomic.Store(): For loading from and storing into atomic variables.
  • atomic.Add(): For atomically adding an integer value.
  • atomic.Swap(): For atomically swapping values.
  • atomic.CompareAndSwap(): For atomically comparing and swapping values.

Using the `atomic` Package with Integers

Let’s start with an example that shows how to use atomic functions for an integer counter:


package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    var counter int32 = 0

    atomic.AddInt32(&counter, 1)
    fmt.Println("Counter:", counter)

    val := atomic.LoadInt32(&counter)
    fmt.Println("Loaded Counter:", val)
}

In this example, we use atomic.AddInt32() to increment an integer in a thread-safe manner and atomic.LoadInt32() to safely read the current value of the counter.

Using the `atomic` Package with Pointers

The `atomic` package also supports operations with pointers.


package main

import (
    "fmt"
    "unsafe"
    "sync/atomic"
)

func main() {
    type customStruct struct {
        data string
    }

    var original = &customStruct{data: "Hello"}
    var atomicPointer unsafe.Pointer

    atomic.StorePointer(&atomicPointer, unsafe.Pointer(original))

    loadedData := (*customStruct)(atomic.LoadPointer(&atomicPointer))
    fmt.Println("Loaded Data:", loadedData.data)
}

Here, we are using atomic.StorePointer() to set a pointer to a custom struct, and atomic.LoadPointer() to atomically retrieve it. Note that we need to convert to and from unsafe.Pointer.

When to Use `sync/atomic`

Use `sync/atomic` when you need simple operations for shared variables without introducing the overhead of mutexes. However, for more complex synchronization involving multiple variables, consider using the sync package’s mutexes and other higher-level concurrency primitives.

Remember that while the `sync/atomic` operations are powerful, they require careful use to avoid introducing bugs. Always validate your approach meets the problem's concurrency requirements.

Next Article: Concurrency Patterns: Fan-in and Fan-out in Go

Previous Article: Deadlocks in Go: How to Detect and Prevent Them

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant