Является ли горутина Go сопрограммой?

В презентации Google I/O 2012 – Шаблоны параллелизма в Go, Роб Пайк упоминает, что несколько горутин могут находиться в одном потоке. Означает ли это, что они реализованы как сопрограммы? Если нет, то как они реализуются? Ссылки на исходный код приветствуются.


person Sławosz    schedule 05.08.2013    source источник


Ответы (4)


Не совсем. В разделе FAQ по Go Почему горутины вместо потоков? поясняется:

Горутины являются частью упрощения использования параллелизма. Идея, которая существует уже некоторое время, состоит в том, чтобы мультиплексировать независимо выполняющиеся функции — сопрограммы — в набор потоков. Когда сопрограмма блокируется, например, вызывая блокирующий системный вызов, среда выполнения автоматически перемещает другие сопрограммы в том же потоке операционной системы в другой исполняемый поток, чтобы они не были заблокированы. Программист ничего этого не видит, в этом и смысл. Результат, который мы называем горутинами, может быть очень дешевым: у них мало накладных расходов, кроме памяти для стека, которая составляет всего несколько килобайт.

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

person K Z    schedule 21.10.2013
comment
Начиная с версии 1.3 стек golang переключился с сегментированной модели на непрерывную модель. См. примечание к выпуску Go 1.3. Собственно, FAQ обновился соответственно :-) - person updogliu; 28.02.2019

ИМО, сопрограмма подразумевает поддержку явных средств для передачи управления другой сопрограмме. То есть программист программирует сопрограмму таким образом, когда он решает, когда сопрограмма должна приостановить выполнение и передать управление другой сопрограмме (либо путем ее вызова, либо путем возврата/выхода (обычно называемого уступкой)).

Горутины Go — это другое дело: они неявно передают управление в определенных неопределенных точках1, которые происходят, когда горутина собирается заснуть на каком-то (внешнем) ресурсов, таких как завершение ввода-вывода, отправка канала и т. д. Этот подход в сочетании с обменом состоянием по каналам позволяет программисту писать логику программы в виде набора последовательных облегченных процессов, что устраняет распространенную проблему спагетти-кода. как к сопрограммам, так и к подходам, основанным на событиях.

Что касается реализации, я думаю, что они очень похожи на (к сожалению, не слишком известную) библиотеку State Threads, просто на более низком уровне (поскольку Go не полагается на libc или подобные вещи и взаимодействует напрямую с ядром ОС) — вы можете прочитать вводный документ по библиотеке ST, где концепция довольно хорошо объяснена.


1 На самом деле эти точки менее определены, чем у сопрограмм, но более определены, чем у настоящих потоков ОС в упреждающая многозадачность, при которой каждый поток может быть приостановлен ядром в любой заданный момент времени и в потоке управления потоком.
Обновление от 28 мая 2021 г. : на самом деле, начиная с Go 1.14, горутины планируются (почти) упреждающе . Однако следует отметить, что это все еще не то самое жесткое вытеснение, которое обычное ядро ​​делает с потоками, которыми оно управляет, но оно гораздо ближе, чем раньше; по крайней мере, теперь горутина не может стать невыгружаемой после входа в цикл занятости.

person kostix    schedule 05.08.2013
comment
Сноска 1 очень полезна. Спасибо. - person updogliu; 28.02.2019
comment
Ваша интерпретация сопрограмм, кажется, совпадает с моей. Ваш ответ заставил меня понять, что сопрограммы не параллельны. (Им просто не нужен глобальный стек вызовов для передачи управления.) - person Alexey; 14.03.2019
comment
@Алексей, вы также можете найти это классическое эссе полезен — помимо прочего, он хорошо справляется со стеком и кучей. - person kostix; 14.03.2019
comment
@Alexey Совместные подпрограммы являются параллельными, но не могут быть параллельными. В отличие от общего английского слова concurrent, в программировании / CS параллелизм означает возможность выполнения вне порядка (в отличие от последовательного), но не обязательно параллельно. - person Eyal Roth; 25.01.2020
comment
@EyalRoth, согласно тому, что я понял из этого ответа, сопрограммы не могут выполняться не по порядку, так как управление передается явно. - person Alexey; 25.01.2020
comment
@Alexey Вы можете разработать сопрограммы, чтобы последовательно передавать управление друг другу, но есть и другие конструкции. Например, есть одна центральная сопрограмма, которая вызывает каждую из других рабочих сопрограмм, которые работают некоторое время, а затем возвращают управление центральной сопрограмме. Центральная сопрограмма сама выбирает, в каком порядке выполнять рабочие сопрограммы (он может быть даже случайным). - person Eyal Roth; 26.01.2020
comment
@EyalRoth, я все еще не понимаю вашей точки зрения: в вашем примере с центральной сопрограммой нет параллелизма. Например, использование генераторов Python не является примером параллелизма. - person Alexey; 26.01.2020
comment
@Alexey Рабочие сопрограммы выполняются в неопределенном порядке. Конечно, каждая сопрограмма всегда выполняется в детерминированном порядке по отношению к самой себе, но не по отношению к другим рабочим сопрограммам. - person Eyal Roth; 26.01.2020
comment
@EyalRoth, я полагаю, это зависит от определения сопрограмм. В этом ответе предполагается, что сопрограммы явно передают управление, поэтому нет недетерминизма: сопрограммы решают, когда и кому они передают управление. - person Alexey; 26.01.2020
comment
@Alexey Передача управления явно не обязательно означает детерминированность. В моем примере рабочие сопрограммы всегда (детерминистически) передают управление центральной сопрограмме, но центральная может динамически (недетерминистически) решить передать управление любой рабочей сопрограмме в каждый момент времени. - person Eyal Roth; 26.01.2020
comment
@EyalRoth, вы путаете динамическое решение с недетерминированным. Если центральная сопрограмма решает передать управление себе, управление передается детерминировано. - person Alexey; 27.01.2020
comment
@ Алексей, я не уверен, что вы здесь называете детерминированным. Центральная сопрограмма может определить, какой рабочей сопрограмме передать управление, в соответствии с генератором случайных чисел, алгоритмом балансировки работы, событиями ввода-вывода или чем-то еще. Это возможно, если рабочие сопрограммы не зависят друг от друга для выполнения своих собственных задач. В этом суть параллелизма. - person Eyal Roth; 27.01.2020
comment
@EyalRoth, используйте, если генератор случайных чисел не делает программу параллельной. Недетерминизм — это не случайность. В параллельной программе порядок выполнения частично не определен/не указан, и некоторые части могут выполняться параллельно. Если центральная сопрограмма передает управление, а затем ей нужно вернуть его, прежде чем она сможет продолжить работу, это не параллелизм. - person Alexey; 27.01.2020
comment
@ Алексей Я полагаю, вы путаете детерминизм с сотрудничеством. В вытесняющей многозадачности (потоках) программа полагается на ОС, чтобы решить, когда приостановить одновременно выполняющиеся задачи; в невытесняющей (кооперативной) многозадачности программа определяет, как разделить каждую из своих параллельных задач (сопрограмм). В обоих случаях достигается параллелизм; т. е. порядок выполнения задач динамичен и (часто) непредсказуем. - person Eyal Roth; 27.01.2020

Является ли горутина правильной сопрограммой или чем-то похожим, часто обсуждается на https://groups.google.com/forum/?fromgroups=#!forum/golang-nuts. Кто-то может поспорить о таких тонкостях, но для большинства так: горутина есть сопрограмма.

Взгляните на https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit чтобы понять, как работает планировщик.

person Volker    schedule 05.08.2013

Горутина — это отдельный «поток» выполнения. Это ИМО не совсем сравнимо с сопрограммой. В первом приближении горутины могут быть реализованы настоящими потоками ОС. Насколько я знаю, так было с ранними версиями gccgo. Еще одно отличие состоит в том, что горутины могут быть вытеснены.

Текущие компиляторы Go реализуют горутины как очень легкие «потоки» пользовательского пространства. Одна отличительная особенность, например. зеленые потоки заключается в том, что горутины могут переключаться на разные потоки ОС.

Я думаю, вы можете найти некоторые интересные моменты здесь: proc.c

person zzzz    schedule 05.08.2013
comment
Что означает preempted? - person Sławosz; 05.08.2013
comment
@Sławosz: см. en.wikipedia.org/wiki/Preemption_(computing). Короче говоря, есть совместное планирование (сопрограммы, yield,...) и упреждающее планирование (потоки ОС, горутина,...) - person zzzz; 05.08.2013
comment
Я не верю, что горутины запланированы заранее. Я думал, что горутины просто имеют определенные точки, в которых они проверяют планировщик на предмет возможного переключения контекста (например, при вызовах функций, при вводе-выводе и т. д.). - person weberc2; 09.06.2016