В чем разница RWMutex и Mutex в Go и когда использовать каждый
Что проверяют
Понимаете ли вы, что RWMutex выигрывает не всегда, и знаете ли его подводные камни.
Как отвечать
Mutex даёт взаимное исключение: одновременно внутри только один участник, независимо от того, читает он или пишет.
RWMutex различает режимы: RLock пускает сколько угодно читателей одновременно, Lock пускает одного писателя и блокирует всех.
Когда RWMutex оправдан: чтений заметно больше, чем записей, и сама критическая секция достаточно длинная. Классический случай это кэш конфигурации, который читают тысячи горутин и обновляют раз в минуту.
Когда он проигрывает, и это главная часть ответа:
- накладные расходы выше: внутри два счётчика и более сложная логика, поэтому на коротких секциях
RWMutexмедленнее обычного мьютекса даже при перевесе чтений; - писатель может голодать: в Go при ожидающем писателе новые читатели блокируются, что защищает от голодания, но снижает пропускную способность чтений;
- не реентрантный: рекурсивный
RLockвнутри уже захваченного чтения способен привести к взаимной блокировке, если между ними встал писатель.
Практическое правило: начинайте с Mutex. Переходите на RWMutex, только замерив, что блокировка действительно узкое место и чтений на порядок больше. Часто лучшим ответом оказывается вообще не блокировка, а sync.Map, атомарная замена указателя на неизменяемую структуру или канал.
Где спотыкаются
Ставят RWMutex по умолчанию без замеров.
Копируют структуру с мьютексом внутри и получают две независимые блокировки.
Захватывают RLock рекурсивно.
Держат блокировку на время сетевого вызова.