Давно не виделись, как ты? Расстояние до предыдущей статьи прошло более месяца, я не обновил статью, я тоже очень беспокоюсь.
Позвольте мне сообщить вам, я наблюдал в течение этого времениproc.go
Исходный код на самом деле является исходным кодом планировщика. Есть тысячи строк кода, в отличие от карт, каналов и т. д. в прошлом. Это огромный проект, чтобы понять все эти коды. До сих пор не смею сказать, что все понимаю.
Если вы хотите копнуть глубже, это будет бесконечно, поэтому поэтапное исследование здесь. Далее я поделюсь исследованием этого периода времени.
По сути, сегодняшняя статья является лишь введением, и будет опубликовано десять серий статей подряд. Каталог выглядит следующим образом:
Эта серия статей в основном вдохновлена общедоступной учетной записью "основная технология программирования языка Go". В ней есть серия руководств по планировщику Go, которые очень хорошо написаны. Настоятельно рекомендуется прочитать ее, и ее статьи будут будут часто цитироваться позже. Не могу не вставить сюда QR код официального аккаунта, обязательно обратите внимание. Это сокровище, которое я нашел в процессе поиска информации.Я хотел сохранить это в тайне, но мне все равно нужно делиться хорошими вещами со всеми, поэтому я не могу почивать на лаврах.
Начнем сегодняшнюю тему.
Месяц назад г-н Чай Шушан, автор книги «Продвинутое программирование на языке Go», опубликовал на CSDN статью «Язык Go стоит десять лет, Go2 готов к работе» с очень широкой перспективой. Приходится смотреть вниз на дорогу и иногда смотреть вверх на небо.Эта статья относится к разряду "смотреть вверх" и смотреть в небо.Рекомендуется к прочтению.
В статье упоминается первый роман, написанный на языке го, «Ху Вэнь Го». Я искал, смеялся и ругался, было довольно интересно. В книге есть такая фраза:
В языке Go go func — это модуль параллелизма, chan — это механизм координации параллельных модулей, panic и Recover — механизмы обработки ошибок, а defer — это волшебный ход, значительно упрощающий управление ошибками.
Горутины выполняют функции независимо друг от друга в одно и то же время в одном и том же пользовательском пространстве, а каналы используются для связи и синхронизированного управления доступом между горутинами.
В прошлой статье мы говорили о каналах и упомянули, что горутины и каналы являются двумя краеугольными камнями параллельного программирования в Go, поэтому в этой статье основное внимание уделяется горутинам и планировщикам go, которые планируют выполнение горутин.
Предварительное знание
os scheduler
С точки зрения операционной системы, программы, которые мы пишем, в конечном итоге будут преобразованы в серию машинных инструкций, и машина выполнит задачу до тех пор, пока все инструкции будут выполняться последовательно. Сущность, которая выполняет задачу «выполнения инструкций по порядку», — это поток, то есть поток — это сущность, запланированная ЦП, а поток — это сущность, которая фактически выполняет инструкции на ЦП.
При запуске каждой программы создается начальный процесс и запускается поток. Хотя потоки могут создавать дополнительные потоки, эти потоки могут выполняться независимо друг от друга, а планирование на этом уровне выполняется процессором, а не процессом.
Планировщик ОС гарантирует, что ЦП не останется бездействующим, если есть потоки для выполнения. И это также гарантирует, что все исполняемые потоки будут выполняться одновременно. Кроме того, хотя планировщик ОС гарантирует, что вероятность выполнения высокоприоритетных потоков выше, чем у низкоприоритетных, он не может допустить, чтобы низкоприоритетные потоки никогда не получили возможности выполниться. Планировщик ОС также должен принимать быстрые решения для уменьшения задержки.
переключение потоков
Планировщик ОС планирует поток на основе его состояния.Поток имеет три состояния (упрощенная модель):Waiting
, Runnable
or Executing
.
государство | объяснять |
---|---|
Waiting | состояние ожидания. Поток ожидает, что что-то произойдет. Например, ожидание сетевых данных, жесткого диска, вызов API операционной системы, ожидание готовности условия доступа к синхронизации памяти, такого как атомарные, мьютексы. |
Runnable | состояние готовности. Я могу работать до тех пор, пока даю ресурсы ЦП |
Executing | Рабочий статус. Поток выполняет инструкции, чего мы и хотим. |
То, что могут делать потоки, обычно делится на два типа: тип вычислений и тип ввода-вывода.
Вычислительный тип в основном занимает ресурсы ЦП и выполняет вычислительные задачи, такие как разложение большого числа на простые числа. Этот тип задачи не заставляет поток переходить в состояние ожидания.
Тип IO — получение внешних ресурсов, например, через сеть, системные вызовы и т. д. Примитивы управления доступом к синхронизации памяти: мьютексы также можно рассматривать как этот тип. Общей особенностью является необходимость ожидания готовности внешних ресурсов. Задачи типа ввода-вывода заставят поток перейти в состояние ожидания.
Переключение потоков — это процесс, в котором операционная система заменяет поток в состоянии Executing, выполняющийся на ЦП, потоком в состоянии Runnable. Новые добавленные потоки будут находиться в состоянии «Выполнение», а новые потоки могут находиться в состоянии «Ожидание» или «Выполняемый». Потоки, выполняющие вычислительные задачи, перейдут в состояние Runnable, а потоки, выполняющие задачи ввода-вывода, перейдут в состояние ожидания.
Следовательно, задачи с интенсивным вычислением и задачи с интенсивным вводом-выводом имеют разное «отношение» к переключению потоков. Поскольку в задачах с интенсивными вычислениями всегда есть задачи, которые нужно выполнить, или, другими словами, у них всегда есть инструкции для выполнения, процесс переключения потоков заставит его остановить текущую задачу, и потери будут очень большими.
Напротив, поток, предназначенный для задач с интенсивным вводом-выводом, если он переходит в состояние ожидания из-за операции, не влияет на него, удаляя его из ЦП. Более того, вновь замененный поток может продолжать использовать ЦП для выполнения задач. С точки зрения всей операционной системы «ход работы» идет вперед.
Помните, что для планировщика ОС самое главное — не позволять одному ядру ЦП простаивать, старайтесь, чтобы у каждого ядра ЦП были задачи.
Если у вас есть программа, ориентированная на работу с привязкой к вводу-выводу, то переключение контекста будет преимуществом. Как только поток переходит в состояние ожидания, его место занимает другой поток в состоянии «Выполняемый». всегда выполнять работу. Это один из наиболее важных аспектов планирования. Не позволяйте ядру бездействовать, если есть работа (потоки в состоянии Runnable).
Анализ процесса вызова функции
Чтобы понять основанный в основе принципа планировщика GO, понимание процесса вызова функции имеет важное значение. Он включает в себя передачу параметров функциональных параметров, прыжок инструкции CPU, передача возвращаемого значения функции и тому подобное. Это требует определенного понимания языка сборки, потому что только язык сборки может выполнять базовые операции, такие как назначение регистра. Есть также объяснение в некоторых из предыдущих статей, а затем проверять его снова.
Пространство кадра стека функций в основном состоит из параметров функции и возвращаемых значений, локальных переменных и параметров и возвращаемых значений других вызываемых функций.
С точки зрения макросов, спецификация вызовов функций в языке Go относится к картинке в блоге Цао Да:
Сборка Go plan9 передает параметры функции и возвращаемые значения через стек.
При вызове подфункции сначала подготовьте параметры на вершине стека, а затем выполните инструкцию CALL. Инструкция CALL помещает значение регистра IP в стек, что является следующей командой, которая должна быть выполнена после завершения вызова функции.
Затем он войдет в кадр стека вызываемого абонента. Во-первых, вызывающий BP будет помещен в стек, который представляет собой базовый адрес стека, который является нижней частью стека. Верхушка стека и основание стека определяют кадр стека функции.
Инструкция CALL аналогична комбинации некоторых функций PUSH IP и JMP.Сначала текущее значение регистра инструкции IP помещается в стек, а затем инструкция JMP используется для записи адреса вызываемой функции в стек. Регистр IP для достижения прыжков.
Инструкция RET является противоположностью CALL, которая в основном эквивалентна инструкции POP IP, то есть адрес возврата, сохраненный в SP, когда выполняется инструкция CALL, перезагружается в регистр IP для реализации возврата функции.
Первый — это пространство входных параметров и возвращаемых значений, подготовленное перед вызовом функции. Тогда инструкция CALL сначала инициирует операцию принудительной отправки обратного адреса. После входа в вызываемую функцию ассемблер автоматически вставляет инструкции, относящиеся к регистру BP, поэтому регистр BP и адрес возврата находятся рядом друг с другом. Далее идет пространство для локальных переменных текущей функции, включая пространство параметров вызова, которое необходимо подготовить для повторного вызова других функций. Когда вызываемая функция выполняет инструкцию возврата RET, она сначала восстанавливает регистры BP и SP из стека, а затем переходит к выполнению соответствующей инструкции с выбранным адресом возврата.
Вышеприведенные два абзаца описывают главу об ассемблере книги Advanced Programming in Go Language, которая хорошо сказано и рекомендуется к прочтению еще раз.
Как работают горутины
что такое горутина
Горутин можно рассматривать как слой абстракции для добавленного потока, он более легкий, его можно выполнять отдельно. Поскольку этот уровень абстракции Gopher не имеет непосредственного отношения к потоку, мы будем видеть код только в летающей горутине. Операционная система наоборот, контролируйте то, что вам горутина, я был слишком занят, чтобы заморачиваться. Я чувствую себя комфортно, выполняя на нем потоки, основной единицей потока является мое расписание.
Разница между горутиной и потоком
Когда дело доходит до горутины, нельзя обойти стороной тему: в чем разница между ней и потоком?
Справочник [Как работают горутины] говорит нам, что мы можем различать три точки зрения: потребление памяти, создание и уничтожение и переключение.
- использование памяти
Потребление стековой памяти для создания горутины составляет 2 КБ.Во время фактической работы, если места в стеке недостаточно, он будет автоматически расширяться. Для создания потока требуется 1 МБ памяти стека, а также требуется область, называемая «защитной страницей», чтобы изолировать его от пространства стека других потоков.
Для HTTP-сервера, созданного с помощью Go, очень легко создать горутину для обработки каждого входящего запроса. Однако если служба построена на языке, использующем потоки в качестве примитива параллелизма, таком как Java, один поток на запрос слишком расточительно расходует ресурсы, и вскоре произойдет ошибка OOM (OutOfMemoryError).
- создавать и уничтожать
Создание и уничтожение потоков будет иметь огромное потребление, потому что работа с операционной системой находится на уровне ядра, и обычным решением является пул потоков. Поскольку горутинами управляет среда выполнения Go, потребление создания и уничтожения очень мало и осуществляется на уровне пользователя.
- переключать
При переключении потоков необходимо сохранить различные регистры для восстановления в будущем:
16 general purpose registers, PC (Program Counter), SP (Stack Pointer), segment registers, 16 XMM registers, FP coprocessor state, 16 AVX registers, all MSRs etc.
А горутины-переключатели должны сохранять только три регистра: счетчик программ, указатель стека и BP.
В целом переключение потоков занимает 1000-1500 наносекунд, а за одну наносекунду может быть выполнено в среднем 12-18 инструкций. Следовательно, за счет переключения потоков количество выполняемых инструкций сократится на 12000-18000.
Переключение горутины занимает около 200 нс, что эквивалентно 2400-3600 инструкциям.
Таким образом, Goroutines гораздо меньше, чем стоимость переключающих потоков.
М: модель N
Мы все знаем, что среда выполнения Go будет отвечать за рождение и смерть горутин, от создания до уничтожения. Среда выполнения создаст M потоков (единица планирования выполнения ЦП) при запуске программы, и N горутин, созданных после этого, будут выполняться в этих M потоках. Это модель M:N:
В то же время в потоке может работать только одна горутина. Когда горутина заблокирована (например, при отправке данных в канал, упомянутый в предыдущей статье, он заблокирован), среда выполнения отложит выполнение текущей горутины и позволит другим горутинам выполнять ее. Цель состоит в том, чтобы не дать потоку бездействовать и слить каждую каплю масла и воды из процессора.
что такое планировщик
Выполнение программы Go состоит из двух слоев: программы Go, среды выполнения, то есть пользовательской программы и среды выполнения. Такие функции, как управление памятью, связь по каналам и создание горутин, реализуются между ними через вызовы функций. Системные вызовы, сделанные пользовательской программой, будут перехвачены средой выполнения, чтобы помочь ей выполнить работу, связанную с планированием и сборкой мусора.
Пример панорамной связи показан ниже:
Почему планировщик
Планировщик Go, возможно, является одной из самых важных частей среды выполнения Go. Среда выполнения поддерживает все горутины и планирует их через планировщик. Горутины и потоки независимы, но выполнение горутин зависит от потоков.
Эффективность выполнения программы Go неотделима от планирования планировщика.
Основной принцип планировщика
На самом деле, с точки зрения операционной системы, все программы являются многопоточными. Планирование горутин в потоки для выполнения — это просто концепция на уровне времени выполнения, над операционной системой.
Есть три основные структуры для реализации планирования горутин. г, м, р.
g
Представляет горутину, которая включает в себя: несколько полей, представляющих стек горутины, указывающих состояние текущей горутины и указывающих адрес выполняемой в данный момент инструкции, то есть значение ПК.
m
Представляет собой резьбу ядра, включая такие поля, как работающие Goroutines.
p
Представляет виртуальный процессор, который поддерживает g-очередь в состоянии Runnable,m
нужно получитьp
бежатьg
.
Конечно, есть и основная структура:sched
, который дает обзор.
Среда выполнения начинает запускать G: Сборка мусора G, выполнять запланированную G, запускать пользовательский код G и создавать m для запуска G раз. Со временем будет создано больше G, также будет создано больше M.
Конечно, в ранних версиях Go не было структуры p,m
должен быть извлечен из глобальной очереди для запускаg
, поэтому необходимо получить глобальную блокировку.Когда объем параллелизма велик, блокировка становится узким местом. Позднее в реализации великого бога Дмитрия Вёкова добавилосьp
структура. каждыйp
Поддерживайте состояние Runnable самостоятельноg
Очередь, решить исходную проблему глобальной блокировки.
Цель планировщика:
For scheduling goroutines onto kernel threads.
Основная идея планировщика Go:
- повторное использование потоков;
- Ограничьте количество одновременно работающих потоков (исключая блокировку) до N, где N равно количеству ядер ЦП;
- Частные очереди выполнения потока, и их можно запускать из других потоков, крадущих горутины, после того, как поток заблокирован, очереди выполнения могут быть переданы другим потокам.
Зачем вам компонент P, нельзя просто поставить очереди выполнения прямо в M?
You might wonder now, why have contexts at all? Can't we just put the runqueues on the threads and get rid of contexts? Not really. The reason we have contexts is so that we can hand them off to other threads if the running thread needs to block for some reason.
An example of when we need to block, is when we call into a syscall. Since a thread cannot both be executing code and be blocked on a syscall, we need to hand off the context so it can keep scheduling.
Чтобы перевести, когда поток заблокирован, перенесите горутины на P, привязанные к нему, в другие потоки.
Планировщик Go запустит sysmon фонового потока, чтобы обнаружить длительные горутины (более 10 мс) и запланировать их в глобальные очереди выполнения. Это глобальная очередь выполнения с низким приоритетом в качестве наказания.
Обзор
Обычно, когда речь заходит о планировщике Go, упоминается модель GPM, давайте рассмотрим их по порядку.
На картинке ниже представлена информация об оборудовании используемого мной Mac, который имеет только 2 ядра.
Но с гиперпоточностью ЦП 1 ядро может стать 2, поэтому, когда я запускаю программу ниже на Mac, она печатает 4.
func main() {
// NumCPU 返回当前进程可以用到的逻辑核心数
fmt.Println(runtime.NumCPU())
}
Поскольку numcpu возвращает количество логических ядер, а не количество физических ядер, конечный результат 4.
Пройдите после запуска программы, каждое логическое ядро будет присвоено P (логический процессор); в то же время каждый будет назначен P M (машины, экспрессированные потоки ядра), нить ядра по-прежнему запланирована планировщиком ОС.
Подводя итог, когда я запускаю программу Go локально, я получаю 4 системных потока для выполнения задач, и каждый поток связан с P.
При инициализации программа Go будет иметь G (начальная горутина), модуль, который выполняет инструкцию. G будет выполняться на M, потоки ядра запланированы на ядрах ЦП, а G запланировано на M.
G, P, M завершены, и есть еще два важных компонента, которые не были упомянуты: глобальная исполняемая очередь (GRQ) и локальная исполняемая очередь (LRQ). LRQ хранит локальные (то есть конкретные P) исполняемые горутины, а GRQ хранит глобальные исполняемые горутины, которые не были назначены конкретному P.
Планировщик Go является частью среды выполнения Go, встроенной в программу Go и работающей вместе с программой Go. Таким образом, он работает в пространстве пользователя, на один уровень выше ядра. В отличие от упреждающего планирования планировщика Os, планировщик Go использует совместное планирование.
Being a cooperating scheduler means the scheduler needs well-defined user space events that happen at safe points in the code to make scheduling decisions.
Совместное планирование обычно устанавливает точку планирования пользователем.Например, yield в python сообщит планировщику ОС, что он может запланировать меня.
Однако в языке Go планирование горутин выполняется средой выполнения Go, а не пользователем, поэтому мы по-прежнему можем рассматривать планировщик Go как упреждающее планирование, поскольку пользователь не может предсказать, что планировщик будет делать дальше.
Подобно потокам, горутины имеют три состояния (упрощенная версия):
государство | объяснять |
---|---|
Waiting | В состоянии ожидания горутина ожидает, что что-то произойдет. Например, ожидание сетевых данных, жесткого диска, вызов API операционной системы, ожидание готовности условия доступа к синхронизации памяти, такого как атомарные, мьютексы. |
Runnable | Состояние готовности, просто дай М и я могу бежать |
Executing | Рабочий статус. горутина выполняет инструкции на M, что нам и нужно |
Следующая принципиальная схема глобальной работы GPM насмотрелась много, и ее можно сохранить.Прочитав следующий цикл статей, я оглянусь назад, и она до сих пор очень трогательна:
горутина планирование времени
В четырех случаях горутина может быть запланирована, но это не обязательно происходит, это просто означает, что планировщик Go имеет возможность планировать.
ситуация | инструкция |
---|---|
использовать ключевые словаgo
|
go создает новую горутину, и планировщик Go рассмотрит возможность планирования |
GC | Поскольку горутина, выполняющая сборщик мусора, также должна работать на M , планирование обязательно произойдет. Конечно, планировщик Go также будет выполнять множество других задач планирования, таких как планирование горутин, которые не требуют доступа к куче для запуска. GC не заботится о памяти в стеке, он только освобождает память в куче |
системный вызов | Когда Goroutine создает системный вызов, он будет блокировать M, поэтому он будет запланирован, а новый Goroutine будет запланирован одновременно. |
Синхронизированный доступ к памяти | Атомные, мьютексы, операции с каналами и т. д. будут блокировать горутину, поэтому она будет отложена по расписанию. После выполнения условий (например, другие горутины разблокированы) будет запланировано их продолжение. |
work stealing
Задача планировщика go состоит в том, чтобы равномерно распределить все исполняемые горутины на M, работающие на P.
Когда P обнаружит, что в его LRQ нет G, он «украдет» некоторые G у других P для запуска. Посмотрите, какой это дух! После того, как вы закончите свою работу, возьмите на себя инициативу поделиться ею с другими ради общих интересов. это называетсяWork-stealing
, реализованный в Go с версии 1.1.
Планировщик GO использует модель m: n. В любое время m goroutines (g) выделяются на n потоков ядра (m), и эти m работают на логических процессорах (p) с максимумом GomaxProcs. Каждый M должен быть прикреплен к P, а каждый P может работать только один M одновременно. Если m на p блокирует, то ей нужны другие m, чтобы запустить Goroutines в P'S LRQ.
На мой взгляд, приведенная выше картина более яркая, чем обычные, где треугольники представляют M, круги — G, а прямоугольники — P.
На самом деле планировщик Go в каждом раунде планирования находит горутины, которые находятся в runnable, и выполняет их. Порядок поиска следующий:
runtime.schedule() {
// only 1/61 of the time, check the global runnable queue for a G.
// if not found, check the local queue.
// if not found,
// try to steal from other Ps.
// if not, check the global runnable queue.
// if not found, poll network.
}
После обнаружения исполняемой горутины она будет продолжать выполняться до тех пор, пока не будет заблокирована.
Когда выполнение G на P2 заканчивается, он переходит к LRQ, чтобы выполнить следующий G. Если LRQ пуст, это означает, что в локальной рабочей очереди нет G для выполнения, а в GRQ в данный момент нет G. В это время P2 случайным образом выберет P (называемый P1), а P2 «украдет» половину G из LRQ P1.
Преимущество этого в том, что есть больше P, которые могут работать вместе, ускоряя выполнение всех G.
Синхронные/асинхронные системные вызовы
Когда G необходимо выполнить системный вызов, в зависимости от типа вызова, есть два случая для M, к которому он подключен:同步
и异步
.
В случае синхронизации M будет заблокирован, а затем запланирован от P, P не может поддерживать бездельников, а G по-прежнему привязан к M. После этого новый M будет вызываться P, а затем будут выполняться G, ожидающие подачи в LRQ P. После завершения системного вызова G будет добавлен к LRQ P, а M будет «заморожен» и «освобожден» при необходимости.
Для асинхронного случая M не будет заблокирован, а асинхронный запрос G будет передан «прокси»-сетевому опросчику, а G также будет привязан к сетевому опросчику.После завершения системного вызова G вернется к П снова. Поскольку M не заблокирован, он может продолжать выполнять другие G в LRQ.
Можно видеть, что в асинхронном случае планировщик Go успешно преобразует задачи ввода-вывода в задачи ЦП или преобразует переключение потоков на уровне ядра в переключение горутин на уровне пользователя, что значительно повышает эффективность.
The ability to turn IO/Blocking work into CPU-bound work at the OS level is where we get a big win in leveraging more CPU capacity over time.
Планировщик Go, как очень требовательный надзиратель, не даст М бездельничать и всегда позволит вам сделать больше дел различными способами.
В Go со временем можно выполнять больше работы, потому что планировщик Go пытается использовать меньше потоков и делать больше в каждом потоке, что помогает снизить нагрузку на ОС и оборудование.
подводные камни планировщика
Поскольку язык Go является совместным планированием, он не будет принудительно планироваться задачей прерывания ЦП после того, как квант времени израсходован, как поток. Для горутин, которые слишком долго выполняются на языке Go, планировщик Go имеет фоновый поток, который постоянно отслеживает. будет обрабатывать его. Но время установки флага есть только в "прологовой" части функции, и без вызова функции это сделать никак нельзя.
Golang implements a co-operative partially preemptive scheduler.
Так что в некоторых крайних случаях вы попадете в некоторые ловушки. Следующий пример взят из справочника [ловушка планировщика].
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for { x++ }
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
Результат операции: он не может выйти из бесконечного цикла, и последний оператор печати не будет выведен.
Зачем? В приведенном выше примере будут запущены горутины, равные количеству ядер ЦП машины, и каждая горутина будет выполнять бесконечный цикл.
После создания этих горутин функция main выполняетtime.Sleep(time.Second)
утверждение. Когда планировщик Go увидел это заявление, он был так счастлив, что собирался прийти и жить. Это хорошее время для планирования, так что основная горутина запланирована. ранее созданныйthreads
Горутины, просто «одна редька и одна косточка», заполняют и M, и P.
Внутри этих горутин нет таких вызовов, какchannel
,time.sleep
Это то, что заставляет планировщик работать. Проблема, вы можете только позволить этим бесконечным циклам продолжать выполняться.
Также есть решение, уменьшающее потоки на 1:
func main() {
var x int
threads := runtime.GOMAXPROCS(0) - 1
for i := 0; i < threads; i++ {
go func() {
for { x++ }
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
результат операции:
x = 0
Нетрудно понять, что после того, как основная горутина спит в течение одной секунды, она снова пробуждается go schduler, назначает M для продолжения выполнения, печатает строку операторов и завершает работу. После выхода основной горутины все остальные горутины также должны выйти. Так называемый "под прикрытием гнезда полного яйца нет", один урон все повреждения.
Что касается того, почему последний напечатанный x равен 0, предыдущая статья«Цао Да говорит о перестройке памяти»Это было упомянуто здесь, и больше не будет обсуждаться здесь.
Другое решение — добавить предложение в цикл for:
go func() {
time.Sleep(time.Second)
for { x++ }
}()
Это также дает основной горутине возможность запланировать выполнение.
Суммировать
Эта статья, рассматривающая планировщик Go с точки зрения макросов, охватывает много вопросов. В следующих 10 статьях подряд я буду углубляться в исходный код и анализировать его слой за слоем. Быть в курсе!
В справочных материалах много хорошо написанных англоязычных блогов.После того, как вы усвоите основные принципы, прочтение этих статей даст вам знакомые ощущения, и это действительно хорошо!
использованная литература
[Зная ответ, как понять разницу между блокирующим неблокирующим и синхронным асинхронным]?Ууху. Call.com/question/19…
[Научитесь создавать Reactor и Proactor с нуля]book.uban.com/subject/303...
[Перевод шифу босса второго ранга в голенг]сегмент fault.com/ah/119000001…
【Ардан Лабс】Woohoo. AR, но labs.com/blog/2018/0…
[Бумажный анализ планировщика времени выполнения Go]У-у-у. В это время. Колумбия. Квота/~ Ах, хорошо/ в это время 6998…
[Перевод широко распространен]По умолчанию стоит Machine.Open/Go-Schedule...
[Код фермера переворачивает статью]Tickets.WeChat.QQ.com/Yes/BV25 Этот VW Steel…
[горутинный сбор данных]GitHub.com/AR но лаборатории/…
[Статьи серии Dabin Scheduler]Les Sis Better.site/2019/03/10/...
【Масштабируемый дизайн планировщика DOC 2012】docs.Google.com/document//…
【Пост в блоге планировщика Go】По умолчанию используется машина.Откройте /go-schedule…
【кража работы】rakyll.org/scheduler/
[Тони Бай также говорит о планировщиках горутин]Tony Pendulum.com/2017/06/23/...
[Анализ экземпляра отладки Tony BAI]Тони Пендулум.com/2017/11/23/…
[Как работает горутина Тони Бая]Тони Пендулум.com/2014/11/15/…
【Как работают горутины】блог. Вы попали на сайт double.com/posts/how-a…
[Зная ответ Что такое блокирующий, неблокирующий, синхронный, асинхронный? 】Ууху. Call.com/question/26…
[Зная статью, вы полностью понимаете синхронный/асинхронный и блокирующий/неблокирующий]zhuanlan.zhihu.com/p/22707398
【Сетевой опрос Go】morsmachine.dk/netpoller
[Знайте колонку Head First of Golang Scheduler]zhuanlan.zhihu.com/p/42057783
【Птичье гнездо Пять моделей IO】www.colonot.com/2019/07/26/…
【Планировщик запуска】Speaker deck.com/ret и видение…
【планировщик】ПО вилас В. Что/го-расписание…
【Планировщик отслеживания】Woohoo. AR, но labs.com/blog/2015/0…
[Использование трассировки инструмента go]Making.pusher.com/go-tool - вдруг...
【Горутинное путешествие】medium.com/@rite EK Пока IV…
[Видео, объясняющее разницу между конкурентностью и параллелизмом]Woohoo.YouTube.com/watch?V=cn_…
【Ловушка планировщика】Woohoo. Сара такая хорошая, но full.com/2016/06/15/…
[Чтение исходного кода Boya]GitHub.com/ live ah/ Голаны...
【Руководство по серии планировщиков Abo Zhang】Билеты.WeChat.QQ.com/tickets/homepage…
【Цао Да asmshare】GitHub.com/often123/Ашима — это…
[Введение в планировщик Go и проблемы, которые легко упустить из виду]блог woo woo woo.cn on.com/code с txt…
[Анализ исходного кода недавно обнаруженного крупного парня]GitHub.com/changkun/go…