ProgrammingBackend Developer

What are the types of synchronization in Go? How to use sync.Mutex, sync.RWMutex, and sync.WaitGroup, and what nuances are there in each case?

Pass interviews with Hintsage AI assistant

Answer

In Go, the structures from the sync package are used for synchronizing concurrent goroutines, the most common ones being sync.Mutex, sync.RWMutex, and sync.WaitGroup.

sync.Mutex provides mutual exclusion mechanisms when accessing shared data. Its methods are Lock() (locks) and Unlock() (unlocks).

sync.RWMutex extends the capabilities of a regular mutex: allowing parallel reads, but exclusive writes.

sync.WaitGroup is intended for waiting for a group of goroutines to finish. Using Add(int), Done(), and Wait(), you control the lifecycle of parallel work.

For example:

var mu sync.RWMutex data := 0 // Reading mu.RLock() fmt.Println(data) mu.RUnlock() // Writing mu.Lock() data = 42 mu.Unlock()

Nuances:

  • Always use Unlock in a defer block to ensure the mutex is unlocked even during a panic.
  • Make sure that the number of calls to WaitGroup.Done() matches Add().
  • Do not copy WaitGroup, Mutex, and RWMutex!

Trick Question

Can the same sync.Mutex (or RWMutex) be acquired twice in a row in the same goroutine? What happens?

Answer: No, if you call Lock() on the same Mutex twice in a row without an intermediate Unlock(), the goroutine will deadlock forever. In Go, mutexes are non-recursive.

Example:

var mu sync.Mutex mu.Lock() // ... mu.Lock() // DEADLOCK: will block forever, as the same thread already holds the lock

Examples of Real Errors


Story

In a project for a high-load REST API, a developer wrapped the entire request handling in a single mutex. This caused a sharp decline in performance — only one request could be processed at a time, whereas thousands of clients were expected. The reason was ignorance of the difference between Mutex and RWMutex and neglecting parallel reads.


Story

While copying a structure with a Mutex inside by one team member, a copy was accidentally passed to another function. This led to a panic message "sync: copy of sync.Mutex" and crashes in production under heavy load.


Story

In using WaitGroup, failed to call Done() in several goroutines, leading to eternal waiting on Wait(), blocking the main thread. As a result, the service lost availability until a manual restart.