Лид: теория Go до кода на доске
Готовитесь к собеседованию на Go? Ниже — 21 теоретический вопрос, который часто звучит до live-coding. Не список определений, а как устроены горутины, каналы, планировщик и сборщик мусора — то, что отличает уверенный ответ от «слышал, но не копал».
Вопросы сгруппированы по уровням. Старт — с базового. Middle+ — средний и продвинутый. Это шпаргалка редакции, не секретный банк конкретной компании. Воронка с Go разобрана в гайде Авито; соседние раунды — теория алгоритмов и System Design.
GMP помните, а речь срывается — проговорите схему на моке с напарником. Дыру в каналах ловит квиз. Оффер не выдаём.
Базовый уровень
1. Чем отличается var от :=?
Оба способа создают переменные, но работают в разных контекстах.
| Критерий | var | := |
|---|---|---|
| Где можно | Внутри и вне функций | Только внутри функций |
| Тип | Явный или вывод | Всегда выводится |
| Нулевое значение | Да | Нет, нужно присвоить |
| Без значения | var x int |
Нельзя |
var x int // x = 0
var y = "hello" // тип string выведен
func main() {
name := "Gopher" // только внутри функции
age := 30
}
2. Что такое горутина и чем она отличается от потока ОС?
Горутина — легковесный поток выполнения, которым управляет рантайм Go, а не операционная система. Это архитектурное решение языка, не «поток поменьше».
| Характеристика | Горутина | Поток ОС |
|---|---|---|
| Начальный стек | ~2 КБ, растёт | ≥1 МБ, обычно фиксирован |
| Создание | Дешёвое | Дорогое, syscall |
| Контекст | User space | Через ядро |
| Кто планирует | Рантайм Go | Планировщик ОС |
| Сколько живут | Десятки тысяч | Сотни–тысячи |
Модель M:N: множество горутин (G) крутятся на небольшом числе системных потоков (M) через логические процессоры (P).
3. Как работают каналы?
Каналы — типизированные конвейеры для передачи данных между горутинами. Через них обычно избегают гонок без явных блокировок.
ch := make(chan int) // небуферизированный
ch <- 5 // отправка
value := <-ch // получение
close(ch) // закрытие
Пока GMP не уехало с языка
Разницу горутины и потока ОС дешевле сказать вслух, чем зубрить таблицу. Оффер не обещаем.
Средний уровень
4. Как планировщик вытесняет горутину и что такое work-stealing?
Планировщик использует work-stealing: пустой P забирает горутины у соседа. С Go 1.14+ планировщик вытесняющий: долгая горутина может быть прервана, даже если сама не делает вызов в рантайм.
go func() {
// код горутины
}()
На раунде ждут не определение горутины, а вытеснение с 1.14 и work-stealing: пустой P забирает очередь соседа.
5. Чем буферизированные каналы отличаются от небуферизированных?
| Тип | Создание | Отправка | Получение |
|---|---|---|---|
| Без буфера | make(chan T) |
Ждёт получателя | Ждёт данные |
| С буфером | make(chan T, n) |
Ждёт, только если буфер полон | Ждёт, только если буфер пуст |
Как это выглядит
- Без буфера отправитель и получатель встречаются синхронно: оба ждут друг друга.
- С буфером ёмкости 3 значения сначала складываются в очередь. Отправитель не блокируется, пока есть место. Получатель не блокируется, пока очередь не пуста.
6. Что произойдёт при записи в nil-канал?
| Операция | Результат |
|---|---|
ch <- value |
Вечная блокировка |
<-ch |
Вечная блокировка |
close(ch) |
panic: close of nil channel |
Nil-канал компилируется. Это ловушка рантайма. Для неблокирующих операций берут select с default.
7. Что такое интерфейс в Go и чем он отличается от Java?
Интерфейс — набор сигнатур методов. Тип удовлетворяет ему неявно, если реализует все методы. Ключевого слова implements нет.
type Writer interface {
Write([]byte) (int, error)
}
type MyWriter struct{}
func (m MyWriter) Write(data []byte) (int, error) {
return len(data), nil
}
// MyWriter удовлетворяет Writer без implements
| Аспект | Go | Java |
|---|---|---|
| Реализация | Неявная | implements |
| Чужой тип | Можно закрыть методами | Только свои классы |
| Содержимое | Только методы | Методы, константы, default |
| Пустой | any / interface{} |
Object |
8. Для чего нужна функция defer?
defer откладывает вызов до выхода из окружающей функции. Порядок — LIFO: последний отложенный вызов выполняется первым. Аргументы считаются сразу, в точке defer.
func example() {
defer fmt.Println("1") // третьим
defer fmt.Println("2") // вторым
fmt.Println("3") // первым
}
// Вывод: 3 2 1
9. Как эффективно соединить много строк?
Берите strings.Builder: меньше аллокаций. Конкатенация через + в цикле каждый раз создаёт новую строку — строки в Go неизменяемы.
var builder strings.Builder
for i := 0; i < 1000; i++ {
builder.WriteString("data")
}
result := builder.String()
Канал на бумажке и канал вслух
Nil-канал и буфер лучше объяснить напарнику, чем пересказать таблицу. Оффер не обещаем.
Продвинутый уровень
10. Как устроены каналы внутри?
В рантайме канал — структура hchan. На раунде ждут не все поля наизусть, а очередь ждущих и буфер под мьютексом.
| Поле | Смысл |
|---|---|
qcount / dataqsiz | Сколько элементов в буфере и какой у него размер |
buf | Указатель на кольцевой буфер |
sendx / recvx | Индексы отправки и получения |
recvq / sendq | Очереди ждущих получателей и отправителей |
lock | Мьютекс канала |
11. Как устроен планировщик в Go?
Планировщик — модель M:N. G — горутина. M — системный поток. P — логический процессор с локальной очередью. Work-stealing: пустой P ворует работу. С 1.14 — вытеснение.
GMP коротко
- Много G ждут в очередях.
- Каждый P держит свою локальную очередь.
- M исполняет G, привязанный к P.
- Пустой P забирает половину очереди у соседа.
12. Какая модель многозадачности в Go?
CSP: communicating sequential processes. Формула с раунда: не общайтесь через общую память — общайтесь сообщениями. Горутины независимы, каналы синхронизируют. Разделяемая память допустима, но не основной паттерн.
13. Как устроен сборщик мусора?
GC в Go — неблокирующий concurrent mark-and-sweep с триколорной маркировкой.
Фазы
- Mark Start — короткий STW.
- Concurrent Mark — параллельная маркировка.
- Mark Termination — короткий STW.
- Concurrent Sweep — очистка мёртвого.
| Цвет | Смысл |
|---|---|
| Белый | Ещё не проверен, кандидат на удаление |
| Серый | Сам проверен, ссылки ещё нет |
| Чёрный | Объект и ссылки живые |
Write barrier: если чёрный объект получает ссылку на белый, белый становится серым. GOGC задаёт целевой рост кучи, по умолчанию 100%.
14. Как устроены слайсы?
Слайс — дескриптор из трёх полей: указатель на массив, len, cap. Добавление сверх cap копирует данные в новый массив, обычно вдвое больше.
| Аспект | Массив | Слайс |
|---|---|---|
| Размер | [3]int |
[]int |
| В функцию | Копируется целиком | Копируется дескриптор |
| Сравнение | == |
Только с nil |
15. Как устроена map?
Map — хеш-таблица с бакетами. Бакет держит до 8 пар. Переполнение даёт цепочку. Ресайз инкрементальный: старые бакеты эвакуируются постепенно. Итерация рандомизирована — на порядок рассчитывать нельзя.
16. Как устроены строки?
Строка — неизменяемая последовательность байт: указатель и длина. Кодировка по умолчанию UTF-8. Неизменяемость позволяет делить память. Любая «правка» — новая строка.
17. Как устроены интерфейсы?
Интерфейс — два указателя: tab (тип и таблица методов) и data (значение). Ловушка: nil-интерфейс не равен интерфейсу, в котором лежит nil-указатель конкретного типа — tab уже заполнен, поэтому iface != nil.
18. Что такое data race и как её обнаружить?
Data race — одновременный доступ к одной памяти из нескольких горутин, и хотя бы одно обращение — запись. Ловят флагом -race. Лечат каналами, sync.Mutex / RWMutex, атомиками.
go run -race main.go
go test -race ./...
19. Что такое sync.Pool?
Пул объектов для повторного использования. Снижает давление на GC при короткоживущих буферах. Объект из пула GC может выкинуть в любой момент — на наличие рассчитывать нельзя.
var bufPool = sync.Pool{
New: func() any { return make([]byte, 1024) },
}
buf := bufPool.Get().([]byte)
bufPool.Put(buf)
20. Как работает context?
Пакет context отменяет работу «вниз по дереву», ставит дедлайн и редко носит значения. Ключи для WithValue должны быть сравнимыми и не строками из чужого пакета.
| Функция | Зачем |
|---|---|
| WithCancel | Контекст и функция отмены |
| WithTimeout | Отмена через интервал |
| WithDeadline | Отмена к моменту времени |
| WithValue | Редкие данные запроса, не конфиг |
21. Что такое линкинг времени выполнения и как init() связан с этим?
Перед main рантайм рекурсивно инициализирует импорты, считает глобальные переменные и вызывает init. В пакете их может быть несколько — в порядке объявления. Явно вызвать init нельзя.
Быстрая шпаргалка
| Тема | Короткий ответ |
|---|---|
| Горутина | ~2 КБ стека, планирует рантайм |
| Буфер канала | Блокирует при полном или пустом |
| nil-канал | Вечная блокировка; close — panic |
| Планировщик | M:N, work-stealing, 1.14+ |
| Модель | CSP |
| GC | Триколор, concurrent mark-and-sweep |
| Слайс | Указатель + len + cap |
| Map | Бакеты, инкрементальный ресайз |
| Строка | Неизменяемая, UTF-8 |
| Интерфейс | tab + data |
| defer | LIFO |
| data race | -race |
| sync.Pool | Пул, GC может выкинуть |
| context | Отмена вниз по дереву |
| init() | До main, явно не вызвать |
На раунде чаще всего: почему десятки тысяч горутин, буфер vs без буфера, nil-канал, фазы GC, слайс/map/iface под капотом, data race и context.
Частые вопросы
Нажмите на вопрос — ответ откроется ниже.
Нет. Это легковесная единица рантайма. Модель M:N, стек около 2 КБ, планирует Go, не ядро.
Отправка и получение блокируются навсегда. close(nil) даёт panic. Это не ошибка компиляции.
Нет. Если в интерфейсе лежит nil-указатель конкретного типа, tab заполнен, и iface != nil.
Порядок намеренно рандомизирован. Код, который ждёт стабильный обход, ломается на раунде и в проде.
Нет отдельного симулятора Яндекса или Авито. Есть мок и квиз. Бот @it_careergym_ru_bot — канал мэтча, не ментор «как в FAANG». Оффер не обещаем.
Что дальше
Теория Go — не экзамен на зубрёжку полей hchan. Нужно выбрать инструмент и объяснить, почему канал, а не мьютекс. Дальше — код вслух, алгоритмы и System Design. Это подготовка, не трудоустройство.
Пока слот не открылся
Соберите заявку на мок и закройте одну тему в квизе. Оффер мы не выдаём — формулировки к доске подтянуть можно.
Также по теме: Собеседование в Авито · Теория алгоритмов · Теория System Design · Парный мок · Плейбук последней недели