Горутины
Last updated
Was this helpful?
Last updated
Was this helpful?
Горутины (goroutines) представляют параллельные операции, которые могут выполняться независимо от функции, в которой они запущены. Главная особенность горутин состоит в том, что они могут выполняться параллельно. То есть на многоядерных архитектурах есть возможность выполнять отдельные горутины на отдельных ядрах процессора, тем самым горутины будут выполняться паралелльно, и программа завершится быстрее.
Горутина (goroutine) — это функция, выполняющаяся конкурентно с другими горутинами в том же адресном пространстве.
Запустить горутину очень просто:
go
normalFunc(args...)
Функция normalFunc(args...)
начнет выполняться асинхронно с вызвавшим ее кодом.
Обратите внимание, горутины очень легковесны. Практически все расходы — это создание стека, который очень невелик, хотя при необходимости может расти.
Каждая горутина, как правило, представляет вызов функции, и последовательно выполняет все свои инструкции. Когда мы запускаем программу на Go, мы уже работаем как минимум с одной горутиной, которая представлена функцией main. Эта функция последовательно выполняет все инструкции, которые определены внутри нее.
Каждая программа на Go по умолчанию создает одну главную горутину — main() . Горутины выполняются поочередно, за их планирование отвечает планировщик Go. Переключение контекста между горутинами происходит в момент блокировки выполнения текущей горутины. Блокировки могут происходить при использовании примитивов для блокировок или при чтении/записи в канал. Функция time.Sleep() также блокирует выполнение функции, по этому при ее вызове происходит переключение контекста на другие горутины.
Анонимные горутины
Анонимные горутины — те же анонимные функции, только вызваны используя ключевое слово go . Давайте добавим спинер ожидания, чтобы разбавить нашу програму.
является перехватывающим задачи (work-stealing) планировщиком, который был введен еще в Go 1.1 Дмитрием Вьюковым вместе с командой Go. Его диздок доступен и включает рассуждения на тему возможных будущих улучшений. Существует множество , помогающих разобраться, как он работает, но основная суть заключается в том, что он пытается управлять G, M и P; горутинами, машинами (потоками) и процессорами.
«G» - это просто горутина Golang.
«M» - это поток ОС, который может выполнять что-либо или же бездействовать.
«P» можно рассматривать как ЦП в планировщике ОС; он представляет ресурсы, необходимые для выполнения нашего Go кода, такие как планировщик или состояние распределителя памяти.
В рантайме они представлены как структуры: , или .
Основная задача планировщика состоит в том, чтобы сопоставить каждую G (код, который мы хотим выполнить) с M (где его выполнять) и P (права и ресурсы для выполнения).
Когда M прекращает выполнение нашего кода, он возвращает свой P в пул свободных P. Чтобы возобновить выполнение Go кода, он должен повторно заполучить его. Точно так же, когда горутина завершается, объект G возвращается в пул свободных G и позже может быть повторно использован для какой-либо другой горутины.
Первое поле структуры g
имеет тип stack
.
Сам стек представляет собой не что иное, как два значения, обозначающих его начало и конец.
К этому времени вы, вероятно, зададитесь вопросом: «А каков же размер этого стека?», или уже догадаетесь, что 2 килобайта относятся к этому стеку!
Кооперативная многозадачность
Модель N:M
Компилятор знает про используемые регистры
2kb памяти на стек для горутины (linux)
Выделение памяти при запуске
Каналы
Блокирующие системные вызовы
Сборка мусора
Вызов функций (сплит стека)
Нетполлер (net pool)
Добровольно (runtime.gosched)
Горутина с минимального размера стека в , который увеличивается и уменьшается по мере необходимости без риска когда-либо закончиться.
отличная статья Дэйва Чейни более подробно объясняет, как это работает. По сути, перед выполнением любой функции Go проверяет, доступен ли объем стека, необходимый для функции, которую он собирается выполнить; если нет, то выполняется вызов который выделяет новую страницу, и только после этого выполняется функция. Наконец, когда эта функция завершается, ее возвращаемые аргументы копируются обратно в исходный фрейм стека, а все невостребованное пространство стека высвобождается.
Хотя минимальный размер стека определен как 2048 байтов, рантайм Go также не позволяет горутинам превышать ; этот максимум зависит от архитектуры и составляет систем.
Если этот предел достигнут, будет выполнен вызов .