📕
Golang
  • Собеседование по golang
  • Вопросы собеседования
    • Список вопросов МТС
    • Список вопросов
    • Базовые вопросы по Golang
    • Go. Прорабатываем 25 основных вопросов собеседования
    • Функции
    • Структуры данных
    • Конкурентность и Параллелизм
    • Горутины
    • Примитивы синхронизации
    • Планировщик
    • Go: конкурентность и привязки к потокам в планировщике
    • Каналы
    • GC
  • спецификация
    • Спецификация Go: преобразования в и из строкового типа
    • переключатель типов (type switch)
    • for утверждения (for statements)
    • for утверждения с range условием
    • go утверждения (go statements)
    • select утверждения (select statements)
    • return утверждения (return statements)
    • continue утверждения (continue statements)
    • goto утверждения (goto statements)
    • fallthrough утверждения (fallthrough statements, утверждения "провала")
    • defer утверждения (defer statements)
    • встроенные функции, функция close
    • длина и емкость
    • аллокация, создание срезов (slice), карт (map) и каналов
    • добавление в срезы и копирование срезов
    • удаление элементов карты
    • обработка паники
    • начальная загрузка (bootstrapping)
    • пакеты
    • инициализация и выполнение программы, нулевое значение
    • инициализация пакета
    • выполнение программы
    • ошибки
    • паника во время выполнения (run-time panic)
  • Эффективный go
    • эффективный go
    • 50 оттенков go
    • Go: распространенные антипаттерны
    • Визуализация concurrency в Go с WebGL
  • требования для работы
    • Список навыков
  • habr
    • Изучаем многопоточное программирование в Go по картинкам
    • Go: конкурентность и привязки к потокам в планировщике
  • NP
    • Полиморфизм с интерфейсами в Golang
    • Объектно-ориентированное программирование в Golang
    • Владеешь merge  -  освой и rebase
  • ProgLib
    • Горутины
  • Untitled
  • Оптимизация
    • Go: Должен ли я использовать указатель вместо копии моей структуры?
  • Полезняшки
    • Using PostgreSQL JSONB with Go
Powered by GitBook
On this page
  • Горутины
  • Переключение между горутинами

Was this helpful?

  1. Вопросы собеседования

Горутины

PreviousКонкурентность и ПараллелизмNextПримитивы синхронизации

Last updated 4 years ago

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.

type g struct {
	// Параметры стека.
	// stack описывает фактическую память стека: [stack.lo, stack.hi).
	// stackguard0 - указатель стека, сравниваемый по мере роста стека Go.
	// stackguard1 - указатель стека, сравниваемый по мере роста стека C.
...
	stack       stack  // смещение, известное runtime/cgo
	stackguard0 uintptr // смещение, известное liblink
	stackguard1 uintptr // смещение, известное liblink

Сам стек представляет собой не что иное, как два значения, обозначающих его начало и конец.

type stack struct {
	lo uintptr
	hi uintptr
}

К этому времени вы, вероятно, зададитесь вопросом: «А каков же размер этого стека?», или уже догадаетесь, что 2 килобайта относятся к этому стеку!

Горутины

  1. Кооперативная многозадачность

  2. Модель N:M

  3. Компилятор знает про используемые регистры

  4. 2kb памяти на стек для горутины (linux)

  5. Выделение памяти при запуске

Переключение между горутинами

  1. Каналы

  2. Блокирующие системные вызовы

  3. Сборка мусора

  4. Вызов функций (сплит стека)

  5. Нетполлер (net pool)

  6. Добровольно (runtime.gosched)

Горутина с минимального размера стека в , который увеличивается и уменьшается по мере необходимости без риска когда-либо закончиться.

отличная статья Дэйва Чейни более подробно объясняет, как это работает. По сути, перед выполнением любой функции Go проверяет, доступен ли объем стека, необходимый для функции, которую он собирается выполнить; если нет, то выполняется вызов который выделяет новую страницу, и только после этого выполняется функция. Наконец, когда эта функция завершается, ее возвращаемые аргументы копируются обратно в исходный фрейм стека, а все невостребованное пространство стека высвобождается.

Хотя минимальный размер стека определен как 2048 байтов, рантайм Go также не позволяет горутинам превышать ; этот максимум зависит от архитектуры и составляет систем.

Если этот предел достигнут, будет выполнен вызов .

Планировщик горутин (Goroutine scheduler)
здесь
замечательных
ресурсов
type g
type m
type p
стартует
2 килобайта
Эта
runtime.morestack,
максимальный размер стека
1 ГБ для 64-разрядных систем и 250 МБ для 32-разрядных
runtime.abort