Ответ
Go предоставляет богатый набор инструментов для управления конкурентным доступом к данным. Основные примитивы находятся в пакете sync
.
-
Каналы (Channels)
- Назначение: Основной идиоматический способ коммуникации и синхронизации между горутинами. Реализуют принцип "Не общайтесь, разделяя память; разделяйте память, общаясь" ("Don't communicate by sharing memory; share memory by communicating").
- Пример:
ch := make(chan string) go func() { ch <- "данные" }() msg := <-ch // Горутина блокируется, пока не получит данные из канала fmt.Println(msg)
-
Мьютекс (
sync.Mutex
)- Назначение: Защита критической секции — участка кода, который в один момент времени может исполнять только одна горутина. Используется для предотвращения гонки данных при доступе к общим ресурсам.
- Пример:
var mu sync.Mutex var counter int
// Внутри горутины mu.Lock() // Захватываем блокировку counter++ // Безопасно изменяем общие данные mu.Unlock() // Освобождаем блокировку
-
Мьютекс чтения-записи (
sync.RWMutex
)- Назначение: Оптимизированный мьютекс для сценариев, где чтений гораздо больше, чем записей. Позволяет неограниченному числу горутин одновременно читать данные, но запись требует эксклюзивной блокировки.
- Пример:
var rwMu sync.RWMutex var config map[string]string
// Чтение (может выполняться параллельно) rwMu.RLock() value := config["key"] rwMu.RUnlock()
// Запись (эксклюзивный доступ) rwMu.Lock() config["key"] = "new_value" rwMu.Unlock()
-
Группа ожидания (
sync.WaitGroup
)- Назначение: Блокировка горутины до тех пор, пока группа других горутин не завершит свою работу. Используется для ожидания завершения набора параллельных задач.
- Пример:
var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) // Увеличиваем счетчик горутин go func() { defer wg.Done() // Уменьшаем счетчик по завершении // ... какая-то работа }() } wg.Wait() // Ожидаем, пока счетчик не станет равен нулю
-
Атомарные операции (
sync/atomic
)- Назначение: Низкоуровневые операции (сложение, загрузка, сохранение), которые выполняются без прерываний. Они намного быстрее мьютексов, но применимы только к простым числовым типам и указателям. Идеальны для счетчиков или флагов.
- Пример:
var counter int64 atomic.AddInt64(&counter, 1) // Атомарный инкремент
-
sync.Once
- Назначение: Гарантирует, что определенный участок кода будет выполнен ровно один раз, независимо от количества параллельных вызовов. Часто используется для ленивой инициализации синглтонов или других ресурсов.
- Пример:
var once sync.Once var dbConnection *DB
func GetConnection() *DB { once.Do(func() { dbConnection = connectToDatabase() }) return dbConnection }
-
Условная переменная (
sync.Cond
)- Назначение: Более сложный примитив, позволяющий горутинам ожидать наступления определенного события или условия. Горутины могут приостановить свое выполнение (
Wait()
) до тех пор, пока другая горутина не оповестит их о том, что условие выполнено (Signal()
илиBroadcast()
).
- Назначение: Более сложный примитив, позволяющий горутинам ожидать наступления определенного события или условия. Горутины могут приостановить свое выполнение (