Ответ
Модель конкурентности в Go основана на концепции Communicating Sequential Processes (CSP) Тони Хоара. Основная идея заключается в том, что взаимодействие между независимыми процессами (горутинами) происходит через каналы, а не через разделение общей памяти.
Go предоставляет следующие ключевые инструменты:
1. Горутины (Goroutines)
Это легковесные потоки, управляемые планировщиком Go (Go runtime), а не ядром ОС.
- Дешевизна: Создание горутины требует гораздо меньше ресурсов, чем создание системного потока. Стеки горутин начинаются с малого размера (около 2 КБ) и могут расти по мере необходимости.
- Простота: Запустить горутину очень просто — достаточно использовать ключевое слово
go
перед вызовом функции.
2. Каналы (Channels)
Каналы — это типизированные "трубы", через которые горутины могут безопасно обмениваться данными.
- Безопасность: Использование каналов помогает избежать состояний гонки, так как в каждый момент времени только одна горутина имеет доступ к данным в канале.
- Типы: Каналы могут быть небуферизированными (блокируют отправителя до тех пор, пока получатель не будет готов) и буферизированными (позволяют отправить несколько значений без блокировки, пока буфер не заполнится).
// Буферизированный канал на 10 элементов ch := make(chan int, 10) ch <- 42 // Отправка значения в канал value := <-ch // Получение значения из канала
3. select
Конструкция select
позволяет горутине ждать операции на нескольких каналах одновременно. Это похоже на switch
, но для каналов.
select {
case msg1 := <-ch1:
fmt.Println("received", msg1)
case ch2 <- "message":
fmt.Println("sent message")
default:
// Выполняется, если ни одна из операций не готова
fmt.Println("no communication")
}
4. Пакет sync
Хотя каналы являются предпочтительным способом, Go также предоставляет традиционные примитивы синхронизации в пакете sync
для случаев, когда работа с общей памятью более уместна.
sync.Mutex
: для защиты критических секций кода (взаимное исключение).sync.WaitGroup
: чтобы дождаться завершения работы группы горутин.sync.Once
: для гарантии, что определенный код выполнится только один раз.
Главный принцип Go: "Do not communicate by sharing memory; instead, share memory by communicating." (Не общайтесь через общую память; вместо этого делитесь памятью через общение).
Для обнаружения состояний гонки при компиляции используется флаг -race
.