> Что такое конкурентность в программировании (Go)

Уровень: junior · Роль: backend · Категория: Технические вопросы

Компании: Моторика

Стек: Go

> Пример ответа

Короткий ответ

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

Подробное объяснение

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

В Go конкурентность строится на трёх китах:

  • горутины - лёгкие потоки, которые управляются рантаймом, а не операционной системой. Стек горутины растёт динамически, а запуск стоит порядка нескольких килобайт памяти;
  • каналы - типизированные "трубы" для передачи данных между горутинами. Каналы обеспечивают синхронизацию и безопасный обмен информацией;
  • примитивы синхронизации - sync.Mutex, sync.WaitGroup, sync.Once и другие для случаев, когда каналы не подходят.

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

Рантайм Go использует модель M:N - много горутин маппятся на меньшее количество OS-потоков. Планировщик распределяет горутины по потокам, приостанавливая одни и запуская другие. Это позволяет запускать десятки тысяч горутин без деградации производительности.

На практике

В backend-разработке на Go конкурентность применяется повсеместно:

  • обработка HTTP-запросов - каждый запрос обрабатывается в отдельной горутине;
  • фан-аут/фан-ин - запуск нескольких параллельных операций (например, запросы к разным сервисам) и сбор результатов;
  • worker pool - фиксированное число горутин, которые берут задачи из очереди;
  • таймауты и отмены через контекст (context.Context).

Важно помнить про гонки данных (data races) - когда несколько горутин одновременно читают и пишут одну переменную. Для обнаружения используй go run -race. Для защиты - мьютексы или атомики, но чаще лучше пересмотреть дизайн и использовать каналы.

Пример кода

GO
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for j := range jobs {
time.Sleep(100 * time.Millisecond) // имитация работы
results <- j * 2
}
}
func main() {
const numJobs = 10
const numWorkers = 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
// запускаем воркеров
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// отправляем задачи
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// ждём завершения всех воркеров
wg.Wait()
close(results)
// собираем результаты
for r := range results {
fmt.Println(r)
}
}

Как отвечать на собеседовании

Начни с определения и сразу разведи конкурентность и параллелизм - это покажет понимание основ. Затем расскажи про горутины и каналы, упомяни, что горутины легче потоков ОС. Приведи пример из практики: обработка запросов или параллельные вызовы API. Если спросят про синхронизацию - упомяни sync.WaitGroup и мьютексы, но подчеркни, что в Go принято отдавать предпочтение каналам. Не углубляйся в детали планировщика, если не просят - для junior достаточно понимания модели.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание разницы между конкурентностью и параллелизмом;
  • знание базовых примитивов Go: горутины, каналы, sync.WaitGroup;
  • осознание проблем: гонки данных, дедлоки, утечки горутин;
  • умение объяснить, когда использовать каналы, а когда мьютексы;
  • способность написать простой конкурентный код без ошибок.

Для junior достаточно уверенного ответа на уровне "горутина - это лёгкий поток, канал - для обмена данными". Если упомянешь context.Context и select - это плюс, но не обязательно.

Типичные ошибки

  • путать конкурентность с параллелизмом или считать их синонимами;
  • говорить, что горутины - это потоки ОС (это не так);
  • забывать про гонки данных и не использовать -race;
  • использовать time.Sleep для синхронизации вместо каналов или WaitGroup;
  • закрывать канал из нескольких горутин - это паника;
  • не вызывать wg.Add до запуска горутин;
  • игнорировать контекст и отмену операций - в реальном backend это критично.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью