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

Как реализована конкурентность в Go?

1.3 Junior🔥 221 комментариев
#Конкурентность и горутины#Основы Go

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

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

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

Реализация конкурентности в Go

Конкурентность в языке Go реализована через модель легковесных процессов (goroutines) и коммуникацию через каналы (channels), что основано на принципе "Do not communicate by sharing memory; instead, share memory by communicating".

Goroutines: легковесные процессы

Goroutine — это функция или метод, которая выполняется независимо от других goroutines. Это не поток ОС, а более легковесная абстракция, управляемая планировщиком Go (scheduler). Для создания goroutine используется ключевое слово go перед вызовом функции.

func main() {
    // Старт goroutine
    go printNumbers()
    
    // Основная goroutine продолжает работу
    fmt.Println("Main goroutine")
}

func printNumbers() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i)
    }
}

Планировщик Go мультиплексирует множество goroutines на небольшое количество потоков ОС, что позволяет эффективно использовать ресурсы. Goroutines имеют:

  • Малые затраты на создание (~2KB памяти)
  • Быстрый старт и завершение
  • Автоматическое управление стеком (динамически расширяется)

Каналы (Channels): безопасная коммуникация

Канал — это типобезопасный конвейер для передачи данных между goroutines. Каналы обеспечивают синхронизацию без явных блокировок.

func main() {
    // Создание канала
    ch := make(chan int)
    
    // Goroutine отправляет данные
    go func() {
        ch <- 42
    }()
    
    // Основная goroutine получает данные
    value := <-ch
    fmt.Println("Received:", value)
}

Каналы могут быть:

  • Буферизованные (make(chan int, 5)): хранят несколько значений без блокировки отправляющей стороны
  • Небуферизованные: требуют одновременной готовности отправляющей и принимающей goroutines

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

Оператор select позволяет goroutine ожидать операций на нескольких каналах, подобно switch для каналов.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from ch1" }()
    go func() { ch2 <- "from ch2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

Модель памяти и синхронизация

Go использует модель памяти, которая гарантирует определенное поведение при конкурентном чтении/записи. Для традиционной синхронизации также доступны:

  • Мьютексы (sync.Mutex, sync.RWMutex)
  • Группы ожидания (sync.WaitGroup)
  • Пулы goroutines с sync.Pool
var counter int
var mu sync.Mutex

func increment() {
    mu.Lock()
    counter++
    mu.Unlock()
}

Планировщик Go (Scheduler)

Планировщик Go реализует cooperative scheduling с прерыванием на границах функций и определенных операций. Он использует:

  • M (Machine): поток ОС
  • G (Goroutine): легковесный процесс
  • P (Processor): контекст выполнения, который связывает M и G

Планировщик распределяет goroutines между доступными потоками ОС, обеспечивая равномерную нагрузку и эффективное использование многопроцессорных систем.

Практические паттерны

  1. Worker pools: группа goroutines обрабатывает задачи из общего канала
func worker(id int, jobs <-chan int, results chan<- int) {
    for job := <-jobs {
        results <- job * 2
    }
}
  1. Fan-out/fan-in: распределение работы между goroutines и сбор результатов
  2. Контекст и отмена: использование context.Context для управления жизненным циклом goroutines
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

Преимущества модели Go

  • Простота: goroutines легче использовать чем классические потоки
  • Безопасность: каналы предотвращают многие race conditions
  • Эффективность: низкие накладные расходы, автоматическое масштабирование
  • Интегрированность: конкурентность — часть языка, не требующая внешних библиотек

Ограничения и рекомендации

  • Goroutines не имеют идентификаторов, их нельзя напрямую убить
  • Необходимо избегать утечек goroutines через proper context cancellation
  • Для интенсивных вычислений может потребоваться управление количеством goroutines

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