WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Go · Advanced · question 54 of 100

How can you use ’atomic’ package in Go for low-level synchronization?

📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

The ‘atomic‘ package in Go provides low-level atomic operations for memory synchronization. It helps ensure that concurrent operations on shared variables are handled safely and efficiently, without the use of mutexes or other higher-level synchronization mechanisms.

To use the ‘atomic‘ package, you need to first import it:

import "sync/atomic"

Here, we will discuss some common atomic operations provided by the ‘atomic‘ package and present examples demonstrating their use.

1. **AddInt32/AddInt64**: These functions atomically add an integer value (32-bit or 64-bit) to the given address and return the new value.

var count int32

func increment() {
    newVal := atomic.AddInt32(&count, 1)
    fmt.Println("New value:", newVal)
}

2. **CompareAndSwapInt32/CompareAndSwapInt64**: These functions (for 32-bit and 64-bit integers) atomically compare a variable with a given value—if they match, it sets the variable to a new value and returns true. Otherwise, it returns false.

Consider a simple example of a bank transaction where we need to update the balance atomically:

var balance int64 = 500

func updateBalance(delta int64) {
    for {
        currentBalance := atomic.LoadInt64(&balance) // Read the current balance atomically
        newBalance := currentBalance + delta
        if atomic.CompareAndSwapInt64(&balance, currentBalance, newBalance) {
            fmt.Printf("Balance updated from %d to %dn", currentBalance, newBalance)
            break
        }
    }
}

3. **LoadInt32/LoadInt64 and StoreInt32/StoreInt64**: These functions are used to atomically load and store 32-bit or 64-bit integer values.

var count int32

func incrementAndLoad() {
    atomic.AddInt32(&count, 1)
    newVal := atomic.LoadInt32(&count)
    fmt.Println("New value:", newVal)
}

func storeAndLoad(val int32) {
    atomic.StoreInt32(&count, val)
    newVal := atomic.LoadInt32(&count)
    fmt.Printf("Stored value: %d, Loaded value: %dn", val, newVal)
}

Here is a complete example demonstrating the use of atomic operations to implement synchronization for a simple counter:

package main

import (
    "sync"
    "sync/atomic"
    "time"
)

var count int64

func main() {
    var wg sync.WaitGroup
    wg.Add(10)

    for i := 0; i < 10; i++ {
        go func() {
            incrementCounter(&count)
            wg.Done()
        }()
    }
    
    wg.Wait()
    
    finalCounter := atomic.LoadInt64(&count)
    fmt.Printf("Final counter value: %dn", finalCounter)
}

func incrementCounter(counter *int64) {
    for i := 0; i < 1000; i++ {
        atomic.AddInt64(counter, 1)
        time.Sleep(time.Duration(1 * time.Millisecond))
    }
}

In this example, the ‘atomic.AddInt64‘ function is used to increment the ‘count‘ variable atomically, ensuring that multiple go routines will not cause race conditions. Finally, the ‘atomic.LoadInt64‘ function is used to obtain the final value of the counter safely.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Go interview — then scores it.
📞 Practice Go — free 15 min
📕 Buy this interview preparation book: 100 Go questions & answers — PDF + EPUB for $5

All 100 Go questions · All topics