Go

When should you use sync.Map instead of a map guarded by a Mutex?

Question 229MediumGo 1.22 to 1.25

The docs name two cases where sync.Map is better:

  1. A key is written once and then read many times, as in a grow-only cache.
  2. Multiple goroutines read, write and overwrite disjoint sets of keys.

Everywhere else, a plain map with a Mutex or RWMutex is usually faster and simpler, and it stays type-safe.

Go 1.24 reimplemented sync.Map on a concurrent hash-trie. It now scales much better for mixed workloads and no longer relies on the older read-only map plus dirty map design with promotion.

var sessions sync.Map // effectively map[string]*Session

s, loaded := sessions.LoadOrStore(id, newSession(id))
sess := s.(*Session)

sessions.CompareAndSwap(id, old, updated) // Go 1.20+
sessions.Range(func(k, v any) bool {
	fmt.Println(k, v)
	return true // false stops iteration
})
sessions.Clear() // Go 1.23+

Gotchas:

  • It has no Len.
  • Values are any, which means type assertions everywhere. Wrap it in a small generic type to get type safety back.
  • Range is not a consistent snapshot: each key is visited at most once, but concurrent writes may or may not be observed.
  • LoadOrStore(k, expensive()) evaluates expensive() on every call, even when the key already exists.

More on Concurrency Patterns & sync

All 38 Concurrency Patterns & sync questions