0 day streak
go / intermediate
Snippet

Thread-Safe Map Access Using Read-Write Mutexes

In Go, maps are not safe for concurrent use. If two goroutines write to a map at the same time, or one reads while another writes, the program will panic with a fatal concurrent map write error. To prevent this, we encapsulate the map within a struct alongside a sync.RWMutex. The Lock method blocks all other reads and writes, while RLock allows multiple concurrent readers but blocks writers.

snippet.go
go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
mu sync.RWMutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.v[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Inc("somekey")
}()
}
wg.Wait()
fmt.Println("Value:", c.Value("somekey"))
}
Breakdown
1
type SafeCounter struct
Defines a custom type wrapping a sync.RWMutex and a map, tying the data and its synchronization mechanism together.
2
c.mu.Lock()
Acquires an exclusive write lock. No other goroutine can read or write until Unlock is called.
3
defer c.mu.Unlock()
Ensures the write lock is released when the Inc method returns, preventing deadlocks.
4
c.mu.RLock()
Acquires a read lock. Multiple readers can hold this lock simultaneously, but writers will be blocked.