Назад к вопросам
Middle+
106
questionbank
Что такое runtime в Go и как работает планировщик (scheduler)?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Runtime в Go - это среда выполнения, которая включает в себя:
- Планировщик (scheduler): управляет горутинами и их выполнением на системных потоках.
- Сборщик мусора (garbage collector): автоматически управляет памятью.
- Системные вызовы: обертки над системными вызовами ОС.
- Сетевой ввод/вывод: неблокирующее сетевое взаимодействие.
Планировщик Go реализует модель M:N, где M горутин выполняются на N системных потоках (OS threads). Используется трехкомпонентная модель G-P-M:
- G (Goroutine): легковесный поток исполнения, управляемый планировщиком Go.
- P (Processor): логический процессор, представляющий контекст, необходимый для выполнения горутин (очередь горутин, кэш стека). Количество P по умолчанию равно
runtime.NumCPU(). - M (Machine): системный поток (OS thread), на котором выполняется код.
Принцип работы планировщика:
- Создание горутин: Новые горутины создаются с помощью ключевого слова
go. - Помещение в очередь: Созданная горутина помещается в локальную очередь P или глобальную очередь, если локальная переполнена.
- Выполнение: M привязан к P и выбирает горутину из его локальной очереди (или глобальной, если локальная пуста). M выполняет код горутины.
- Переключение контекста: Происходит, когда горутина блокируется (например, при системном вызове, ожидании мьютекса, операции ввода-вывода) или когда планировщик решает переместить горутину (например, по истечении квоты времени).
- Блокировка M: Если горутина вызывает блокирующий системный вызов, текущий M отключается от P, и планировщик находит или создает новый M для этого P. Заблокированный M возвращается в пул, когда системный вызов завершен.
- Кооперативное вытеснение (Preemption): В старых версиях Go вытеснение было кооперативным (goroutine должна была явно уступить выполнение). В современных версиях (с 1.14) реализовано асинхронное вытеснение на основе сигналов ОС, что позволяет прерывать выполнение горутин, зациклившихся в неблокирующем коде.
- Балансировка нагрузки (Work Stealing): Если у одного P закончились горутины в локальной очереди, он может "украсть" горутины у других P или из глобальной очереди.
Преимущества планировщика Go:
- Высокая производительность: Эффективно использует многоядерные процессоры.
- Легковесные горутины: Позволяют создавать миллионы параллельно работающих "потоков".
- Неблокирующий ввод/вывод: Упрощает написание высокопроизводительных сетевых приложений.
- Простота использования: Разработчику не нужно управлять потоками вручную.
Пример создания горутины:
package main
import (
"fmt"
"time"
)
func worker() {
fmt.Println("Выполняется горутина")
}
func main() {
go worker() // Создание новой горутины
time.Sleep(time.Second) // Ожидание завершения горутины
fmt.Println("Завершена основная горутина")
}