Ответ
В Go изоляция данных между горутинами достигается с помощью нескольких ключевых подходов, которые следуют главному принципу языка: "Не обменивайтесь данными через общую память; обменивайтесь данными через каналы".
1. Каналы (Channels)
Это наиболее идиоматичный способ в Go. Каналы предоставляют механизм для безопасной передачи данных между горутинами. Отправляя данные в канал, одна горутина передает "владение" этими данными другой, что предотвращает гонки данных.
// Горутина безопасно отправляет данные в канал
ch := make(chan int)
go func() {
result := 42 // какая-то работа
ch <- result // передача владения
}()
// Основная горутина безопасно получает данные
val := <-ch
fmt.Println(val) // 42
2. Примитивы синхронизации (пакет sync
)
Когда использование каналов неудобно или неэффективно, применяются традиционные примитивы синхронизации для защиты "критических секций" — участков кода, работающих с общими данными.
-
sync.Mutex
: Обеспечивает эксклюзивный доступ к данным. Только одна горутина может владеть мьютексом в любой момент времени.var mu sync.Mutex var counter int // ... в разных горутинах mu.Lock() // Захватываем блокировку counter++ // Безопасно изменяем общие данные mu.Unlock() // Освобождаем блокировку
sync.RWMutex
: Оптимизированный мьютекс для сценариев, где чтений гораздо больше, чем записей. Позволяет неограниченному числу горутин одновременно читать данные, но запись требует эксклюзивной блокировки.
3. Атомарные операции (пакет sync/atomic
)
Для простых операций, таких как инкремент счетчика или обновление флага, использование мьютекса может быть избыточным. Атомарные операции выполняются без прерываний и являются более легковесными.
var counter int64
// Безопасное увеличение счетчика без блокировок
atomic.AddInt64(&counter, 1)
// Безопасное чтение
currentValue := atomic.LoadInt64(&counter)
Ключевой вывод:
Основная цель — избежать гонок данных (data races). Идиоматичный путь в Go — это каналы, но мьютексы и атомарные операции являются мощными инструментами для других сценариев. Для обнаружения гонок данных всегда используйте флаг -race
при тестировании и запуске: go run -race main.go
.