Горутины и конкурентность

18 вопросов

Горутины - легковесные потоки, управляемые рантаймом Go. Конкурентность - ключевая сила языка. Важно понимать data race, GOMAXPROCS и утечки горутин.

1 Что такое горутина? 🟢 Лёгкий
Ответ: B) Лёгкий поток, управляемый рантаймом Go

Горутина - не поток ОС, а легковесный "зелёный поток", управляемый планировщиком Go. Начальный стек ~2 КБ (у потока ОС ~1 МБ). Можно запустить тысячи горутин. Рантайм мультиплексирует горутины на потоки ОС.

Подробнее →
2 Как запустить функцию в горутине? 🟢 Лёгкий
Ответ: B) go f()

Ключевое слово go запускает функцию в новой горутине. Вызов не блокирует - управление сразу возвращается. Работает с любыми функциями и методами, включая анонимные: go func() { ... }().

Подробнее →
3 Что произойдёт, если main() завершится до горутин? 🟡 Средний
Ответ: B) Программа завершится, горутины остановятся

Когда main() возвращается, процесс завершается - все горутины убиваются. Они не получают сигнала. Для ожидания горутин используйте sync.WaitGroup, каналы или context.

Подробнее →
4 Что такое data race? 🟡 Средний
Ответ: B) Одновременный доступ к памяти без синхронизации

Data race - когда две или более горутины одновременно обращаются к одной переменной и хотя бы одна из них пишет. Результат непредсказуем. Решения: мьютексы, каналы, атомарные операции.

Подробнее →
5 Какой флаг go test обнаруживает data race? 🟡 Средний
Ответ: B) -race

go test -race или go run -race включает детектор гонок. Он замедляет программу, но находит data race во время выполнения. Рекомендуется использовать в CI/CD.

Подробнее →
6 Что такое GOMAXPROCS? 🟡 Средний
Ответ: B) Максимум потоков ОС для параллелизма

GOMAXPROCS определяет, сколько потоков ОС одновременно выполняют Go-код. По умолчанию равен количеству CPU. Это не лимит горутин - горутин может быть гораздо больше.

Подробнее →
7 Что произойдёт при утечке горутины? 🟡 Средний
Ответ: B) Горутина останется в памяти навсегда

Go не имеет механизма принудительного завершения горутин. Если горутина заблокирована (ждёт канал, мьютекс), она останется в памяти до завершения программы. GC не собирает горутины. Используйте context с отменой.

Подробнее →
8 Какой начальный размер стека горутины? 🟡 Средний
Ответ: B) Несколько килобайт, растёт по необходимости

Горутина стартует с маленьким стеком (2-8 КБ в зависимости от версии Go), который автоматически растёт при необходимости. Потоки ОС обычно получают 1-8 МБ фиксированного стека. Поэтому можно запускать тысячи горутин, но не тысячи потоков.

Подробнее →
9 Какая самая частая причина утечки горутин (goroutine leak)? 🟡 Средний
Ответ: B) Горутина навсегда заблокирована на канале без читателя/писателя

Горутина 'утекает', когда навсегда заблокирована - ждёт чтения/записи в канал, который никогда не станет готов. Решения: context с отменой, таймауты, буферизованные каналы. Утечки ведут к утечке памяти. Мониторинг: runtime.NumGoroutine(), pprof goroutine.

Подробнее →
10 Что выведет: for i := 0; i < 3; i++ { go func() { fmt.Print(i) }() } (до Go 1.22)? 🟡 Средний
Ответ: C) Непредсказуемо, но скорее всего 333

До Go 1.22 замыкание захватывало переменную i по ссылке. К моменту выполнения горутин цикл обычно завершён и i == 3. Решение: передать как аргумент: go func(n int){...}(i). С Go 1.22 переменная цикла создаётся заново на каждой итерации.

Подробнее →
11 Что делает runtime.Gosched() и когда его стоит использовать? 🟡 Средний
Ответ: B) Уступает процессор планировщику, позволяя другим горутинам выполниться; на практике редко нужен с Go 1.14+

runtime.Gosched() добровольно отдает управление планировщику. С Go 1.14 (асинхронный preemption) почти не нужен - планировщик сам прервет долгую горутину.

Подробнее →
12 Что такое starvation (голодание) в контексте конкурентности Go? 🟡 Средний
Ответ: B) Ситуация, когда горутина не может получить доступ к ресурсу, потому что другие горутины постоянно его захватывают первыми

Starvation: горутина постоянно проигрывает конкуренцию за ресурс (мьютекс, CPU, канал). Она не заблокирована навсегда (не deadlock), но практически не получает работы.

Подробнее →
13 Что выведет в Go 1.22+: for i := 0; i < 3; i++ { go func() { fmt.Print(i) }() }; time.Sleep(time.Second)? 🔴 Сложный
Ответ: C) 0, 1, 2 в произвольном порядке

До Go 1.22 замыкание захватывало одну переменную i по ссылке - все горутины печатали 3. С Go 1.22 переменная цикла создаётся заново на каждой итерации. Каждая горутина получает свою копию i (0, 1, 2). Порядок зависит от планировщика.

Подробнее →
14 Что произойдёт при одновременной записи в обычный map из нескольких горутин? 🔴 Сложный
Ответ: C) fatal error без возможности recover

Go runtime обнаруживает конкурентный доступ к map и вызывает fatal error, который нельзя перехватить через recover(). Программа завершается немедленно. Для конкурентного доступа используйте sync.Mutex или sync.Map.

Подробнее →
15 Что произойдёт, если горутина пишет в небуферизованный канал, но никто не читает? 🔴 Сложный
Ответ: C) Горутина утечёт навсегда (goroutine leak)

Горутина заблокируется на отправке навсегда - утечка горутины. GC не собирает заблокированные горутины. Каждая утечка расходует ~2-8 КБ памяти. Используйте context для отмены или буферизованные каналы.

Подробнее →
16 Что делает runtime.Goexit()? 🔴 Сложный
Ответ: B) Завершает горутину, deferred функции выполняются

runtime.Goexit() завершает текущую горутину, но все deferred функции выполняются. Это отличает его от os.Exit(), который завершает процесс без defer. Goexit() не влияет на другие горутины.

Подробнее →
17 При каких операциях горутина может уступить управление планировщику Go? 🔴 Сложный
Ответ: B) При операциях с каналами, системных вызовах, аллокациях, вызовах функций и с Go 1.14+ - по preemption-сигналу в любой точке

Горутина уступает управление при: операциях с каналами, сетевых/файловых операциях, sync-примитивах, вызовах функций (проверка стека), runtime.Gosched(), и с Go 1.14 - асинхронно по сигналу.

Подробнее →
18 Что такое livelock и чем он отличается от deadlock? 🔴 Сложный
Ответ: B) При deadlock горутины заблокированы и ждут; при livelock горутины активны, но бесконечно реагируют друг на друга без продвижения к результату

Deadlock: горутины заблокированы, ждут друг друга - программа зависает. Livelock: горутины активны (потребляют CPU), постоянно меняют состояние в ответ друг на друга, но не делают полезной работы.

Подробнее →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.