В каких случаях предпочесть sync.Mutex вместо sync.RWMutex?

Ответ

sync.Mutex следует предпочесть sync.RWMutex в следующих случаях:


  1. Нагрузка с частой записью. Если количество операций записи сопоставимо с количеством операций чтения или превышает его. RWMutex оптимизирован для ситуаций, когда чтений значительно больше, чем записей.



  2. Короткие критические секции. RWMutex имеет более высокие внутренние накладные расходы на координацию (он сложнее, чем Mutex). Если блокировка удерживается на очень короткое время, выигрыш от параллельного чтения может не окупить эти расходы.



  3. Высокая конкуренция (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) // Новые данные
}