Ответ
Go предоставляет богатый набор примитивов синхронизации в пакетах sync
и sync/atomic
. Они используются для безопасной работы с общими данными в конкурентной среде.
Основные примитивы из пакета sync
:
-
sync.Mutex
(Мьютекс)- Назначение: Для обеспечения эксклюзивного доступа к критической секции кода. Только одна горутина может владеть блокировкой в один момент времени.
- Пример:
var mu sync.Mutex mu.Lock() // Блокируем доступ // ... критическая секция ... mu.Unlock() // Освобождаем
-
sync.RWMutex
(Мьютекс чтения-записи)- Назначение: Оптимизация для сценариев, где чтений значительно больше, чем записей. Позволяет неограниченному числу горутин одновременно читать данные, но запись требует эксклюзивной блокировки.
- Пример:
var rwMu sync.RWMutex rwMu.RLock() // Блокировка для чтения rwMu.RUnlock() // Снятие блокировки чтения rwMu.Lock() // Блокировка для записи rwMu.Unlock() // Снятие блокировки записи
-
sync.WaitGroup
- Назначение: Для ожидания завершения работы группы горутин. Основная горутина блокируется до тех пор, пока все дочерние горутины не сообщат о своем завершении.
- Пример:
var wg sync.WaitGroup wg.Add(1) // Увеличиваем счетчик go func() { defer wg.Done() // Уменьшаем счетчик при выходе // ... работа ... }() wg.Wait() // Ждем, пока счетчик не станет 0
-
sync.Once
- Назначение: Гарантирует, что определенный участок кода будет выполнен только один раз за все время работы программы. Идеально для ленивой инициализации синглтонов.
- Пример:
var once sync.Once once.Do(func() { /* код инициализации */ })
-
sync.Pool
- Назначение: Для переиспользования объектов, чтобы снизить нагрузку на сборщик мусора (GC). Полезно для временных объектов, которые часто создаются и уничтожаются.
-
sync.Cond
(Условная переменная)- Назначение: Более сложный примитив, который позволяет горутинам ждать наступления определенного события или условия, широковещательно оповещая ожидающих.
Пакет sync/atomic
- Назначение: Предоставляет низкоуровневые атомарные операции (например,
Add
,Load
,Store
,CompareAndSwap
) для числовых типов. Эти операции выполняются без блокировок и являются самым быстрым способом для простых модификаций (например, счетчиков).
Каналы (chan
)
Хотя каналы — это в первую очередь средство коммуникации, они также являются мощным примитивом синхронизации. Философия Go гласит: "Не общайтесь, разделяя память; разделяйте память, общаясь". Часто использование каналов является более идиоматичным и безопасным способом организации конкурентного кода, чем использование мьютексов.