Что и когда использовать: `sync.Mutex` с `map` или `sync.Map`?

Ответ

В большинстве случаев предпочтительнее использовать обычную map с sync.Mutex (или sync.RWMutex). sync.Map — это оптимизация для очень специфичных сценариев.

map + sync.Mutex/sync.RWMutex

Это стандартный и самый предсказуемый способ защиты map для конкурентного доступа.

  • sync.Mutex: Обеспечивает эксклюзивный доступ. Идеален, когда количество записей и чтений примерно одинаково.
  • sync.RWMutex: Позволяет множество одновременных чтений или одну запись. Оптимален для сценариев, где чтений значительно больше, чем записей (read-heavy).
// Пример с RWMutex
var mu sync.RWMutex
data := make(map[string]string)

// Запись
mu.Lock()
data["key"] = "value"
mu.Unlock()

// Чтение
mu.RLock()
_ = data["key"]
mu.RUnlock()

sync.Map

Используйте sync.Map только при выполнении двух условий одновременно:

  1. Ключи в мапе в основном читаются и очень редко изменяются (сценарий "write-once, read-many").
  2. Высокая степень конкуренции, когда множество горутин одновременно обращаются к мапе.

sync.Map оптимизирована для уменьшения конфликтов блокировок в этих случаях, но имеет свои недостатки: она сложнее, менее производительна в общих случаях и не предоставляет стандартных методов, таких как len().

var sm sync.Map

// Запись
sm.Store("key", "value")

// Чтение
val, ok := sm.Load("key")

Итог: Начинайте с map + sync.RWMutex. Это правильный выбор в 95% случаев. Переходите на sync.Map только после профилирования и выявления явных проблем с производительностью из-за конфликтов блокировок.