Ответ
sync.Mutex
следует предпочесть sync.RWMutex
в следующих случаях:
Нагрузка с частой записью. Если количество операций записи сопоставимо с количеством операций чтения или превышает его.
RWMutex
оптимизирован для ситуаций, когда чтений значительно больше, чем записей.Короткие критические секции.
RWMutex
имеет более высокие внутренние накладные расходы на координацию (он сложнее, чемMutex
). Если блокировка удерживается на очень короткое время, выигрыш от параллельного чтения может не окупить эти расходы.Высокая конкуренция (contention). При большом количестве горутин, одновременно пытающихся захватить блокировку, простой
Mutex
может показать лучшую производительность из-за своей простоты.RWMutex
в таких условиях может привести к голоданию писателей (writer starvation), когда постоянный поток читателей не дает писателю захватить блокировку.
Краткое руководство:
Используйте
sync.Mutex
(по умолчанию):- Когда сомневаетесь.
- Когда операции записи происходят часто.
- Для простых структур данных и коротких блокировок.
Используйте
sync.RWMutex
(как оптимизацию):- Когда операций чтения на порядок больше, чем записей (например, 90% чтений, 10% записей).
- Когда операции чтения достаточно долгие, и их распараллеливание даст заметный прирост производительности.
Пример, где RWMutex
оправдан (конфигурация, которая редко меняется):
package main
import (
"sync"
"time"
)
// Config кэшируется и редко обновляется
var config struct {
mu sync.RWMutex
data map[string]string
}
// GetConfig вызывается очень часто
func GetConfig(key string) string {
config.mu.RLock() // Блокировка на чтение
defer config.mu.RUnlock()
return config.data[key]
}
// UpdateConfig вызывается редко
func UpdateConfig() {
config.mu.Lock() // Эксклюзивная блокировка на запись
defer config.mu.Unlock()
// Имитация обновления
time.Sleep(100 * time.Millisecond)
config.data = make(map[string]string) // Новые данные
}