Ответ
Синхронизация в Go необходима для управления доступом к общим данным из нескольких горутин. Без синхронизации возникает состояние гонки (data race
), когда несколько горутин одновременно обращаются к одной и той же области памяти, и хотя бы одна из них выполняет запись. Это приводит к непредсказуемому поведению и трудноуловимым багам.
Основные примитивы синхронизации в пакете sync
:
sync.Mutex
: Мьютекс (mutual exclusion) для защиты "критической секции" — участка кода, который в один момент времени может выполнять только одна горутина. Используется для защиты общего состояния.sync.RWMutex
: "Читающий-пишущий" мьютекс. Позволяет неограниченному числу горутин одновременно читать данные, но запись возможна только при эксклюзивной блокировке. Эффективен для сценариев с частым чтением и редкой записью.sync.WaitGroup
: Позволяет дождаться завершения работы группы горутин. Основные методы:Add()
для увеличения счетчика горутин,Done()
для уменьшения счетчика (вызывается в конце работы горутины) иWait()
для блокировки до тех пор, пока счетчик не станет равен нулю.sync.Once
: Гарантирует, что определенный участок кода будет выполнен только один раз за все время работы программы. Идеально подходит для инициализации синглтонов.sync.Cond
: Условная переменная, которая позволяет горутинам ожидать наступления определенного события или условия.
Каналы (chan
) как средство синхронизации:
Каналы — это не только способ передачи данных между горутинами, но и мощный механизм синхронизации. Отправка или получение данных из канала блокирует горутину до тех пор, пока на другом конце канала не появится "партнер".
Ключевой принцип Go:
"Не используйте общую память для коммуникации; вместо этого используйте коммуникацию для обмена памятью." (Don't communicate by sharing memory; share memory by communicating.)
Это означает, что передача данных через каналы часто является более идиоматичным и безопасным подходом, чем использование мьютексов и общего состояния.
Пример с Mutex
:
var counter int
var mu sync.Mutex // Мьютекс для защиты counter
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock() // Блокируем доступ для других горутин
// Начало критической секции
counter++
// Конец критической секции
mu.Unlock() // Освобождаем блокировку
}