Concurrency в Go: как работают горутины и каналы

Время чтения: 3 минуты
Хочешь кодить как босс?
Заполняй форму и начни свой путь в IT прямо сейчас!
Concurrency (параллельность) в Go реализуется с помощью горутины — лёгких потоков, и каналов — инструментов обмена данными между ними. Вместо сложных примитивов синхронизации Go предлагает простой, понятный и производительный способ писать конкурентный код — именно это делает язык таким популярным в highload-разработке.
Что такое Concurrency и зачем она нужна
Concurrency — это способность программы выполнять несколько задач одновременно, например: загружать файл, обрабатывать входящий запрос и сохранять данные в базу, не блокируя друг друга.

Это не про «многопоточность» в классическом смысле. Concurrency — про управление задачами, а не ядрами. Go решает эту задачу максимально просто и эффективно. Именно поэтому его так любят backend‑разработчики и компании, работающие с большими нагрузками.

Чем горутины отличаются от потоков

Поток (thread) — тяжёлый объект ОС. Запустить тысячи потоков — дорого и неэффективно.

Горутина (goroutine) — лёгкий поток, управляемый планировщиком Go, а не операционной системой. Они запускаются через ключевое слово go и могут быть десятки тысяч одновременно — это норма.

Каждая горутина занимает всего 2 КБ памяти на старте — в десятки раз меньше, чем поток.
IT-калькулятор зарплат
Узнай свою рыночную зарплату за 1 минуту!
Как запускаются горутины в Go
Горутина — это просто функция, запущенная в фоне:
func sayHello() {
fmt.Println("Привет из горутины!")
}

func main() {
go sayHello()
time.Sleep(time.Second)
}

Важно: main не будет ждать завершения других горутин — поэтому здесь мы используем Sleep.
Что такое каналы и как с ними работать
Канал (channel) — это «труба», через которую горутины обмениваются данными:
ch := make(chan string)

go func() {
ch <- "Привет!"
}()

msg := <-ch
fmt.Println(msg)

Такой подход позволяет избежать гонки данных — одна горутина пишет, другая читает.

Буферизованные vs небуферизованные каналы

  • Небуферизованный канал (make(chan T)) — блокирует отправителя до тех пор, пока получатель не прочитает данные.
  • Буферизованный канал (make(chan T, n)) — хранит n элементов, не блокируя сразу:
ch := make(chan int, 2)
ch <- 1
ch <- 2
// не заблокирует сразу

Выбор зависит от архитектуры: где нужна синхронизация, а где — скорость.
Пример: как горутины и каналы работают вместе
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, j)
time.Sleep(time.Second)
results <- j * 2
}
}

func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)

for i := 1; i <= 3; i++ {
go worker(i, jobs, results)
}

for j := 1; j <= 5; j++ {
jobs <- j
}

close(jobs)

for a := 1; a <= 5; a++ {
fmt.Println(<-results)
}
}

Alt + F4 — и выйти из скучной работы
Начни путь в IT с поддержкой опытного ментора и гарантией трудоустройства. Если сейчас нет возможности оплатить обучение полностью — выбери удобный формат
Механизм select и как он помогает
select позволяет слушать несколько каналов одновременно:

select {
case msg := <-ch1:
fmt.Println("Получено из ch1:", msg)
case msg := <-ch2:
fmt.Println("Получено из ch2:", msg)
default:
fmt.Println("Нет сообщений")
}
Это мощный инструмент построения отказоустойчивых и асинхронных сервисов.
Паттерны конкурентного программирования в Go
Fan-out/Fan-in — запуск нескольких горутин на обработку и сбор результата.
Worker pool — фиксированное количество воркеров обрабатывают задачи из общего канала.
Pipeline — цепочка обработки данных: каждая стадия — горутина.
Основные ошибки при работе с горутинами
Даже опытные разработчики совершают ошибки при работе с параллельностью в Go. Вот самые частые проблемы:

Забывают закрывать каналы. Открыли chan, начали отправлять — и всё. А в другом месте кто-то ждёт данных и зависает, потому что канал не закрыт. Итог — утечка памяти или блокировка.

Используют time.Sleep вместо sync.WaitGroup. Это ненадёжно. Лучше использовать sync.WaitGroup, чтобы дождаться завершения горутин корректно.

Блокируют горутины навсегда. Если канал нечитабельный или переполнен — горутина может зависнуть. Такое бывает, если:
  • канал не читает никто;
  • буфер заполнен;
  • нет select с default-веткой.
Путают каналы и мьютексы. Каналы — не замена мьютексам (sync.Mutex). Они хорошо работают для передачи данных, но не для защиты от одновременного доступа к переменной.

Если нужно защитить состояние — используй мьютекс.
Если передать результат — используй канал.
IT — это не закрытая сфера для избранных. Это рынок, где ценятся навыки, готовность учиться и способность решать задачи. Если ты хочешь войти в IT — тебе не нужен диплом, не нужно быть математиком и не обязательно знать всё заранее. Главное — выбрать направление, пройти практическое обучение, собрать первые проекты и показать, что ты умеешь.

Если тебе важна поддержка на этом пути — Kata Academy предлагает готовый маршрут: обучение, наставники, практика и помощь с выходом на рынок. А главное — ты можешь начать бесплатно и убедиться, подходит ли тебе выбранная профессия.
Новая карьера начинается не с идеального момента, а с одного действия. И, возможно, твой первый шаг — прямо перед тобой.

Статьи для старта в IT

Истории наших выпускников

Стань тем, кто задаёт тон в IT!
Подпишись на нашу рассылку и первым получай статьи по Java, JavaScript, Golang и QA. Позволь себе быть экспертом!