Mutex Locks in Go
In the previous lesson, you learned how WaitGroups help synchronize goroutines. In this lesson, you will learn about Mutex Locks, which are used to protect shared data from being accessed by multiple goroutines at the same time.
What Is a Mutex?
A Mutex (short for mutual exclusion) is a synchronization mechanism
provided by the sync package. It ensures that only one goroutine can access
a critical section of code at a time.
Mutexes are essential when multiple goroutines read and write the same data.
The Problem: Race Conditions
A race condition occurs when multiple goroutines try to access and modify shared data simultaneously, producing unpredictable results.
Consider a bank account balance being updated by multiple transactions at the same time.
Race Condition Example (Without Mutex)
The following example demonstrates a race condition.
package main
import (
"fmt"
"sync"
)
var balance = 1000
func deposit(amount int, wg *sync.WaitGroup) {
defer wg.Done()
balance += amount
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go deposit(100, &wg)
}
wg.Wait()
fmt.Println("Final balance:", balance)
}
The expected result is 1500, but due to concurrent writes, the result may vary.
Using Mutex to Fix the Problem
A mutex ensures that only one goroutine modifies the balance at a time.
var balance = 1000
var mu sync.Mutex
func deposit(amount int, wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
balance += amount
mu.Unlock()
}
Now the balance updates occur safely.
How Mutex Works
Lock()blocks other goroutinesUnlock()releases the lock- Only one goroutine holds the lock at a time
Real-World Example: Order Counter
Imagine an e-commerce system tracking total orders placed.
var orders = 0
var mu sync.Mutex
func placeOrder(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
orders++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go placeOrder(&wg)
}
wg.Wait()
fmt.Println("Total orders:", orders)
}
Mutex prevents inconsistent order counts.
Using defer with Unlock
To avoid forgetting to unlock, use defer.
mu.Lock()
defer mu.Unlock()
// critical section
Deadlocks
A deadlock occurs when a mutex is locked but never unlocked. This causes all other goroutines to wait indefinitely.
Always ensure every Lock() has a corresponding Unlock().
Mutex vs WaitGroup
- Mutex protects shared data
- WaitGroup waits for completion
- They serve different purposes
Practice Exercises
Exercise 1
Simulate a counter that increments 100 times using goroutines and a mutex.
Exercise 2
Modify the program to decrement the counter safely.
Key Takeaways
- Mutex prevents race conditions
- Use
Lock()andUnlock()correctly - Always protect shared variables
- Essential for safe concurrency
What’s Next?
In the next lesson, you will learn about Worker Pools and how to manage concurrent tasks efficiently.