Ответ
В Go есть несколько механизмов синхронизации, каждый из которых подходит для разных сценариев:
- Мьютексы (
sync.Mutex
) – используются для блокировки доступа к общему ресурсу, обеспечивая эксклюзивный доступ для одной горутины в любой момент времени. Идеально подходят для защиты структур данных.
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
defer mu.Unlock() // Гарантирует разблокировку даже при панике
counter++
}
- RWMutex (
sync.RWMutex
) – мьютекс для чтения/записи. Позволяет множественное чтение одновременно, но эксклюзивную запись. Оптимален, когда чтений значительно больше, чем записей.
var rw sync.RWMutex
var data map[string]string
func read(key string) string {
rw.RLock()
defer rw.RUnlock()
return data[key]
}
func write(key, value string) {
rw.Lock()
defer rw.Unlock()
data[key] = value
}
- Каналы (
chan
) – идиоматичный способ для горутин, позволяющий безопасно обмениваться данными и синхронизировать их выполнение, следуя принципу "не обменивайтесь данными путем совместного использования памяти; вместо этого обменивайтесь памятью путем коммуникации".
ch := make(chan int, 1) // Буферизованный канал
// Отправка данных
go func() {
ch <- 42
}()
// Получение данных
val := <-ch
fmt.Println(val)
- Atomic операции (
sync/atomic
) – предоставляют низкоуровневые, атомарные операции для простых типов данных (целые числа, указатели). Используются для счетчиков или флагов, когда не требуется сложная логика блокировки.
var count int32
atomic.AddInt32(&count, 1) // Атомарное инкрементирование
val := atomic.LoadInt32(&count) // Атомарное чтение
- WaitGroup (
sync.WaitGroup
) – используется для ожидания завершения группы горутин. Позволяет основной горутине дождаться выполнения всех запущенных дочерних горутин.
var wg sync.WaitGroup
wg.Add(1) // Увеличиваем счетчик ожидаемых горутин
go func() {
defer wg.Done() // Уменьшаем счетчик по завершении горутины
// Выполнение работы горутины
fmt.Println("Горутина завершила работу")
}()
wg.Wait() // Блокирует до тех пор, пока счетчик не станет равен 0
fmt.Println("Все горутины завершены")
Выбор конкретного механизма зависит от сценария: мьютексы для защиты сложных структур, каналы для коммуникации и координации между горутинами, atomic для простых счетчиков, а WaitGroup для ожидания завершения параллельных задач.