← Назад к вопросам

Какие коммуникации можно строить между горутинами?

2.0 Middle🔥 241 комментариев
#Конкурентность и горутины

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Коммуникации между горутинами в Go

В Go существует несколько основных механизмов для организации коммуникации между горутинами, которые позволяют реализовать безопасное и эффективное взаимодействие в конкурентной среде.

Каналы (Channels)

Каналы — это наиболее фундаментальный и рекомендуемый способ коммуникации. Они реализуют модель CSP (Communicating Sequential Processes) и обеспечивают синхронизацию данных.

// Пример базового канала
ch := make(chan int)

// Горутина отправляет данные
go func() {
    ch <- 42
}()

// Горутина получает данные
value := <-ch

Разновидности каналов:

  • Буферизованные и небуферизованные:
unbuffered := make(chan int)       // Без буфера
buffered := make(chan int, 5)      // Буфер на 5 элементов
  • Направленные каналы (только для отправки или получения):
sendOnly := make(chan<- int)
receiveOnly := make(<-chan int)
  • Каналы с закрытием:
close(ch)
value, ok := <-ch  // ok указывает, закрыт ли канал

Синхронизация через пакет sync

Для более сложных сценариев используются примитивы из пackage sync.

Мьютексы (sync.Mutex, sync.RWMutex):

var mutex sync.Mutex
var sharedData int

go func() {
    mutex.Lock()
    sharedData++
    mutex.Unlock()
}()

RWMutex позволяет оптимизировать чтение при множественных читателях.

WaitGroup (sync.WaitGroup) для ожидания завершения группы горутин:

var wg sync.WaitGroup
wg.Add(2)

go func() {
    defer wg.Done()
    // работа...
}()

wg.Wait()

Once (sync.Once) для гарантированного однократного выполнения:

var once sync.Once
once.Do(initializeFunction)

Cond (sync.Cond) для условной сигнализации:

cond := sync.NewCond(&sync.Mutex{})
// Ожидание условия и сигнал другим горутинам

Контексты (Context)

Пакет context предоставляет механизм для передачи сигналов завершения, значений и deadlines между горутинами.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Передача контекста в горутину
go worker(ctx)

// Проверка завершения
select {
case <-ctx.Done():
    // обработка завершения
default:
    // продолжение работы
}

Атомарные операции (atomic)

Пакет sync/atomic позволяет выполнять низкоуровневые атомарные операции для простых типов.

var counter int64
atomic.AddInt64(&counter, 1)
value := atomic.LoadInt64(&counter)

Select для мультиплексирования

Оператор select позволяет горутине одновременно работать с несколькими каналами.

select {
case msg1 := <-ch1:
    // обработка из ch1
case msg2 := <-ch2:
    // обработка из ch2
case <-time.After(1 * time.Second):
    // таймаут
}

Shared memory с синхронизацией

Горутины могут взаимодействовать через общую память, но это требует обязательной синхронизации через мьютексы или атомарные операции.

var sharedSlice []int
var mu sync.Mutex

go func() {
    mu.Lock()
    sharedSlice = append(sharedSlice, 1)
    mu.Unlock()
}()

Особые техники

  • Worker pools с использованием каналов для распределения задач
  • Pipeline паттерн с цепочкой каналов
  • Pub/Sub модели через каналы и select
  • Barrier синхронизация через каналы или WaitGroup

Критерии выбора механизма

  • Каналы — для передачи данных и естественной синхронизации
  • Мьютексы — когда нужно защищать сложные структуры данных
  • Atomic — для простых счетчиков и флагов
  • Context — для управления жизненным циклом и cancellation
  • WaitGroup/Once — для координации групп горутин

Основной принцип Go: "Do not communicate by sharing memory; instead, share memory by communicating". Каналы являются предпочтительным способом, но в реальных приложениях часто комбинируются разные подходы для оптимального решения задач конкурентности.

Какие коммуникации можно строить между горутинами? | PrepBro