Ответ
В стандартной библиотеке Go, в пакете sync
, есть два основных типа мьютексов:
1. sync.Mutex
(Эксклюзивная блокировка)
Это классический мьютекс, который предоставляет эксклюзивный доступ к общему ресурсу. Только одна горутина может владеть блокировкой в любой момент времени. Любая другая горутина, пытающаяся захватить блокировку, будет заблокирована до тех пор, пока мьютекс не будет освобожден.
Пример использования с defer
:
var mu sync.Mutex
func criticalSection() {
mu.Lock() // Захватываем блокировку
defer mu.Unlock() // Гарантируем освобождение блокировки при выходе из функции
// ... код критической секции ...
}
2. sync.RWMutex
(Блокировка чтения-записи)
Это более сложный мьютекс, который делает различие между операциями чтения и записи:
- Чтение: Любое количество горутин может одновременно получить блокировку на чтение (
RLock
). - Запись: Только одна горутина может получить блокировку на запись (
Lock
), и только если нет активных блокировок на чтение или запись.
Пример использования:
var rwMu sync.RWMutex
// Функция для чтения данных
func readData() {
rwMu.RLock()
defer rwMu.RUnlock()
// ... безопасное чтение данных ...
}
// Функция для записи данных
func writeData() {
rwMu.Lock()
defer rwMu.Unlock()
// ... безопасная запись данных ...
}
Ключевые различия и сценарии использования
Характеристика | sync.Mutex | sync.RWMutex |
---|---|---|
Тип доступа | Эксклюзивный (один "писатель", читателей нет) | Общий для чтения (много читателей) или эксклюзивный для записи (один писатель) |
Производительность | Быстрее и проще, меньше накладных расходов. | Эффективнее в сценариях, где количество чтений значительно превышает количество записей. |
Сложность | Простой. | Более сложный, имеет больший оверхед. |
Когда использовать | Когда операции чтения и записи примерно сбалансированы, или когда любая операция является модифицирующей. | Когда есть интенсивная нагрузка на чтение и редкие операции записи. |
Важные рекомендации
- Используйте
defer
: Всегда используйтеdefer
для вызоваUnlock
/RUnlock
сразу послеLock
/RLock
. Это надежно защищает отdeadlock
в случае паники или несколькихreturn
в функции. - Остерегайтесь "голодания" (Starvation): При использовании
RWMutex
теоретически возможна ситуация, когда постоянный поток читателей не дает писателю захватить блокировку. Реализация в Go пытается быть справедливой, но об этом риске стоит помнить. - Нулевое значение: Мьютексы в Go не нужно явно инициализировать. Их нулевое значение (
zero value
) является готовым к использованию разблокированным мьютексом.