18 вопросов
Горутины - легковесные потоки, управляемые рантаймом Go. Конкурентность - ключевая сила языка. Важно понимать data race, GOMAXPROCS и утечки горутин.
Горутина - не поток ОС, а легковесный "зелёный поток", управляемый планировщиком Go. Начальный стек ~2 КБ (у потока ОС ~1 МБ). Можно запустить тысячи горутин. Рантайм мультиплексирует горутины на потоки ОС.
Подробнее →Ключевое слово go запускает функцию в новой горутине. Вызов не блокирует - управление сразу возвращается. Работает с любыми функциями и методами, включая анонимные: go func() { ... }().
Подробнее →Когда main() возвращается, процесс завершается - все горутины убиваются. Они не получают сигнала. Для ожидания горутин используйте sync.WaitGroup, каналы или context.
Подробнее →Data race - когда две или более горутины одновременно обращаются к одной переменной и хотя бы одна из них пишет. Результат непредсказуем. Решения: мьютексы, каналы, атомарные операции.
Подробнее →go test -race или go run -race включает детектор гонок. Он замедляет программу, но находит data race во время выполнения. Рекомендуется использовать в CI/CD.
Подробнее →GOMAXPROCS определяет, сколько потоков ОС одновременно выполняют Go-код. По умолчанию равен количеству CPU. Это не лимит горутин - горутин может быть гораздо больше.
Подробнее →Go не имеет механизма принудительного завершения горутин. Если горутина заблокирована (ждёт канал, мьютекс), она останется в памяти до завершения программы. GC не собирает горутины. Используйте context с отменой.
Подробнее →Горутина стартует с маленьким стеком (2-8 КБ в зависимости от версии Go), который автоматически растёт при необходимости. Потоки ОС обычно получают 1-8 МБ фиксированного стека. Поэтому можно запускать тысячи горутин, но не тысячи потоков.
Подробнее →Горутина 'утекает', когда навсегда заблокирована - ждёт чтения/записи в канал, который никогда не станет готов. Решения: context с отменой, таймауты, буферизованные каналы. Утечки ведут к утечке памяти. Мониторинг: runtime.NumGoroutine(), pprof goroutine.
Подробнее →for i := 0; i < 3; i++ { go func() { fmt.Print(i) }() } (до Go 1.22)? 🟡 Средний
▶
До Go 1.22 замыкание захватывало переменную i по ссылке. К моменту выполнения горутин цикл обычно завершён и i == 3. Решение: передать как аргумент: go func(n int){...}(i). С Go 1.22 переменная цикла создаётся заново на каждой итерации.
Подробнее →runtime.Gosched() добровольно отдает управление планировщику. С Go 1.14 (асинхронный preemption) почти не нужен - планировщик сам прервет долгую горутину.
Подробнее →Starvation: горутина постоянно проигрывает конкуренцию за ресурс (мьютекс, CPU, канал). Она не заблокирована навсегда (не deadlock), но практически не получает работы.
Подробнее →for i := 0; i < 3; i++ { go func() { fmt.Print(i) }() }; time.Sleep(time.Second)? 🔴 Сложный
▶
До Go 1.22 замыкание захватывало одну переменную i по ссылке - все горутины печатали 3. С Go 1.22 переменная цикла создаётся заново на каждой итерации. Каждая горутина получает свою копию i (0, 1, 2). Порядок зависит от планировщика.
Подробнее →Go runtime обнаруживает конкурентный доступ к map и вызывает fatal error, который нельзя перехватить через recover(). Программа завершается немедленно. Для конкурентного доступа используйте sync.Mutex или sync.Map.
Подробнее →Горутина заблокируется на отправке навсегда - утечка горутины. GC не собирает заблокированные горутины. Каждая утечка расходует ~2-8 КБ памяти. Используйте context для отмены или буферизованные каналы.
Подробнее →runtime.Goexit()? 🔴 Сложный
▶
runtime.Goexit() завершает текущую горутину, но все deferred функции выполняются. Это отличает его от os.Exit(), который завершает процесс без defer. Goexit() не влияет на другие горутины.
Подробнее →Горутина уступает управление при: операциях с каналами, сетевых/файловых операциях, sync-примитивах, вызовах функций (проверка стека), runtime.Gosched(), и с Go 1.14 - асинхронно по сигналу.
Подробнее →Deadlock: горутины заблокированы, ждут друг друга - программа зависает. Livelock: горутины активны (потребляют CPU), постоянно меняют состояние в ответ друг на друга, но не делают полезной работы.
Подробнее →