Ответ
Основной механизм для безопасного обмена данными и синхронизации между горутинами в Go — это каналы (channels). Этот подход соответствует ключевой философии Go:
"Do not communicate by sharing memory; instead, share memory by communicating."
(Не общайтесь через разделяемую память; вместо этого, разделяйте память через общение.)
Канал представляет собой типизированный конвейер, через который можно отправлять и получать значения определенного типа, обеспечивая потокобезопасность.
Типы каналов:
- Небуферизированные (синхронные):
make(chan T)
- Требуют одновременного участия отправителя и получателя (рандеву).
- Гарантируют, что отправка и получение произошли одновременно.
- Буферизированные (асинхронные):
make(chan T, capacity)
- Имеют внутренний буфер для хранения данных.
- Позволяют отправителю продолжать работу, не дожидаясь получателя, пока буфер не заполнится.
// Создание канала для строк
ch := make(chan string)
// Горутина-писатель
go func() {
// Отправка значения в канал
ch <- "Привет из горутины!"
}()
// Горутина-читатель (в данном случае - main)
// Блокируется до получения значения
message := <-ch
fmt.Println(message)
Основные операции и возможности:
- Типобезопасность: Канал может передавать значения только того типа, с которым он был создан.
- Блокировка: Операции отправки и получения по умолчанию блокирующие, что является основой для синхронизации.
- Закрытие канала: Функция
close(ch)
сигнализирует, что больше данных отправлено не будет. Это позволяет читателям завершить свою работу. - Мультиплексирование: Оператор
select
позволяет горутине ожидать операции сразу на нескольких каналах, что является мощным инструментом для реализации сложной логики конкурентности.