DeployU
Interviews / Backend Engineering / What is the difference between `sync.Mutex` and `sync.RWMutex` in Go?

What is the difference between `sync.Mutex` and `sync.RWMutex` in Go?

conceptual Concurrency Interactive Quiz Code Examples

The Scenario

You are a backend engineer at a social media company. You are building a new service that has a shared resource that is read by many goroutines and written to by a few goroutines.

You need to find a way to protect the shared resource from concurrent access.

The Challenge

Explain the difference between sync.Mutex and sync.RWMutex in Go. What are the pros and cons of each approach, and which one would you choose for this use case?

Wrong Approach

A junior engineer might just use a `sync.Mutex` for all use cases. They might not be aware of the `sync.RWMutex` or the performance benefits of using a read-write mutex.

Right Approach

A senior engineer would know that a `sync.RWMutex` is the perfect tool for this job. They would be able to explain the difference between a `sync.Mutex` and a `sync.RWMutex`, and they would be able to explain the performance benefits of using a read-write mutex.

Step 1: Understand the Key Differences

Featuresync.Mutexsync.RWMutex
LockingProvides an exclusive lock. Only one goroutine can hold the lock at a time.Provides a read-write lock. Multiple goroutines can hold a read lock at the same time, but only one goroutine can hold a write lock at a time.
Use CasesWhen you need to protect a shared resource from concurrent access, and the resource is written to frequently.When you have a shared resource that is read frequently and written to infrequently.

Step 2: Choose the Right Tool for the Job

For our use case, a sync.RWMutex is the best choice. It will allow multiple goroutines to read the shared resource at the same time, which will improve performance.

Step 3: Code Examples

Here are some code examples that show the difference between the two approaches:

sync.Mutex:

package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mutex   sync.Mutex
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mutex.Lock()
            counter++
            mutex.Unlock()
        }()
    }
    wg.Wait()
    fmt.Println(counter)
}

sync.RWMutex:

package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mutex   sync.RWMutex
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mutex.RLock()
            fmt.Println(counter)
            mutex.RUnlock()
        }()
    }
    wg.Wait()
}

Practice Question

You have a shared resource that is written to frequently and read infrequently. Which of the following would be the most appropriate?