Go⚡Concurrencyintermediate
sync.Mutex and sync.RWMutex
Go · Concurrency · Run with go run
Explanation
When goroutines share mutable state (instead of communicating via channels), a mutex serializes access. `sync.Mutex` provides exclusive locking (`Lock`/`Unlock`). `sync.RWMutex` allows multiple concurrent readers OR one exclusive writer — use it when reads vastly outnumber writes.
Real-World Scenario
A concurrent cache stores results of expensive API calls. Multiple goroutines read from the cache simultaneously (RLock), but when a cache miss triggers a fill, one goroutine holds the write lock (Lock) while others wait — preventing duplicate fetches and data races.
Quick Snippet
snippet.go
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
// RWMutex for read-heavy cache
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) (string, bool) {
c.mu.RLock() // multiple readers allowed simultaneously
defer c.mu.RUnlock()
v, ok := c.data[key]
return v, ok
}Full Example
mutex.go
// Compile: go run mutex.go
// Output:
// final count: 1000
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
func main() {
c := &SafeCounter{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Inc()
}()
}
wg.Wait()
fmt.Println("final count:", c.Value())
}
Run command
go run mutex.go