Skip to main content

Command Palette

Search for a command to run...

Golang Concurrency #8 - Mutexes

Updated
2 min read
Golang Concurrency #8 - Mutexes
T

Just a guy who loves to write code and watch anime.

Introduction

Mutexes solve the problem: "What happens when multiple goroutines access the same variable?"

The Problem: Race Condition

var counter int

func main() {
    for i := 0; i < 1000; i++ {
        go func() {
            counter++  // Multiple goroutines modifying same variable
        }()
    }

    time.Sleep(time.Second)
    fmt.Println("Counter:", counter)  // Should be 1000, but isn't!
}

Why it breaks

Goroutine 1: Read counter (0) → Add 1 → Write back (1)
Goroutine 2: Read counter (0) → Add 1 → Write back (1)

Both read 0 at the same time, both write 1. We lost an increment!

The Solution: Mutex (Mutual Exclusion)

var counter int
var mutex sync.Mutex

func main() {
    for i := 0; i < 1000; i++ {
        go func() {
            mutex.Lock()    // "I need exclusive access"
            counter++       // Only one goroutine can do this at a time
            mutex.Unlock()  // "I'm done, others can proceed"
        }()
    }

    time.Sleep(time.Second)
    fmt.Println("Counter:", counter)  // Now correctly prints 1000
}

How Mutex works

  • Lock() - "Wait until no one else is using this, then it's mine"

  • Unlock() - "I'm done, next goroutine can have it"

  • Only one goroutine can hold the lock at a time

Better pattern with defer

func increment() {
    mutex.Lock()
    defer mutex.Unlock()  // Automatically unlock when function exits

    counter++
    // Even if panic happens, mutex gets unlocked
}

RWMutex (Read/Write Mutex)

var data map[string]int
var rwMutex sync.RWMutex

func readData(key string) int {
    rwMutex.RLock()         // Multiple readers allowed
    defer rwMutex.RUnlock()
    return data[key]
}

func writeData(key string, value int) {
    rwMutex.Lock()          // Exclusive write access
    defer rwMutex.Unlock()
    data[key] = value
}

When to use mutexes

  • Protecting shared variables (counters, maps, slices)

  • When you need to update data structures

  • When channels would be overkill

Remember

  • Always pair Lock() with Unlock()

  • Use defer to ensure unlocking

  • Keep critical sections (locked code) small and fast

  • Prefer channels when possible, use mutexes when necessary