Ответ
Основное отличие sync.RWMutex
от sync.Mutex
заключается в том, что RWMutex
предоставляет два типа блокировок: одну для записи (эксклюзивную) и одну для чтения (разделяемую).
RWMutex
стоит использовать, когда нагрузка на защищаемый ресурс преимущественно состоит из операций чтения, а операции записи происходят значительно реже. Это позволяет множеству горутин одновременно читать данные, что повышает производительность.
Ключевые сценарии использования:
Частое чтение, редкая запись: Классический случай. Если у вас есть конфигурация, кэш или другие данные, которые читаются постоянно, а изменяются редко. Эмпирическое правило — количество чтений превышает количество записей как минимум на порядок (в 10+ раз).
Длительные операции чтения: Если операция чтения сама по себе занимает значительное время (например, обход большого среза или мапы),
RWMutex
позволит этим операциям выполняться параллельно, в то время какMutex
заставил бы их ждать друг друга.Высокая конкуренция за ресурс: Когда множество горутин одновременно пытаются получить доступ к данным для чтения.
Пример: Кэш в памяти
package main
import (
"sync"
"time"
)
var (
cache = make(map[string]string)
rwMu = sync.RWMutex{}
)
// Get позволяет множеству горутин читать одновременно
func Get(key string) string {
rwMu.RLock() // Блокировка на чтение
defer rwMu.RUnlock()
// Имитация долгой операции чтения
time.Sleep(10 * time.Millisecond)
return cache[key]
}
// Set блокирует всех (и читателей, и писателей) для эксклюзивного доступа
func Set(key, value string) {
rwMu.Lock() // Блокировка на запись
defer rwMu.Unlock()
cache[key] = value
}
Важные моменты и "подводные камни":
- Голодание писателей (Writer Starvation): Если поток читателей непрерывен, горутина, ожидающая блокировки на запись, может никогда её не получить. В стандартной реализации
sync.RWMutex
в Go этот риск снижен, так как она отдает предпочтение ожидающему писателю перед новыми читателями, но о такой возможности стоит помнить. - Накладные расходы:
RWMutex
сложнее и медленнее, чемMutex
. Если критическая секция очень маленькая и быстрая, или если количество записей сопоставимо с количеством чтений, производительность сRWMutex
может оказаться хуже, чем с обычнымMutex
из-за дополнительных затрат на координацию.
Вывод: Выбор между Mutex
и RWMutex
— это компромисс. RWMutex
эффективен для сценариев "много читателей, мало писателей", но в других случаях простой Mutex
может быть более производительным и предсказуемым решением.