На чем основана модель конкурентности в Go и какие инструменты она предоставляет?

Ответ

Модель конкурентности в 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.