Разница между «сопрограммой» и «нитью»?

Ответы:

122

Сопрограммы являются формой последовательной обработки: в каждый момент времени выполняется только одна (точно так же, как подпрограммы AKA выполняют процедуры AKA - они просто передают эстафету друг другу более плавно).

Потоки являются (по крайней мере концептуально) формой параллельной обработки: несколько потоков могут выполняться в любой момент времени. (Традиционно на одноядерных, одноядерных машинах этот параллелизм моделировался с некоторой помощью ОС - в настоящее время, так как очень много машин являются многопроцессорными и / или многоядерными, потоки будут де-факто выполняться одновременно, не просто "концептуально").

Алекс Мартелли
источник
188

Сначала прочитайте: Параллелизм против Параллелизма - Какая разница?

Параллелизм - это разделение задач для обеспечения чередующегося выполнения. Параллелизм - это одновременное выполнение нескольких частей работы для увеличения скорости. - https://github.com/servo/servo/wiki/Design

Краткий ответ: В случае потоков операционная система преимущественно переключает запущенные потоки в соответствии со своим планировщиком, который является алгоритмом в ядре операционной системы. С сопрограммами программист и язык программирования определяют, когда переключать сопрограммы; иными словами, задачи совместно выполняются многозадачностью путем приостановки и возобновления функций в заданных точках, обычно (но не обязательно) в одном потоке.

Длинный ответ: В отличие от потоков, которые предварительно планируются операционной системой, переключатели сопрограмм работают совместно, то есть программист (и, возможно, язык программирования и его среда выполнения) контролирует, когда произойдет переключение.

В отличие от потоков, которые имеют приоритет, переключатели сопрограмм работают совместно (программист контролирует, когда произойдет переключение). Ядро не участвует в сопрограммных переключателях. - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

Язык, который поддерживает собственные потоки, может выполнять свои потоки (пользовательские потоки) в потоках операционной системы (потоки ядра ). Каждый процесс имеет как минимум один поток ядра. Потоки ядра похожи на процессы, за исключением того, что они совместно используют пространство памяти в своем собственном процессе со всеми другими потоками в этом процессе. Процесс «владеет» всеми назначенными ему ресурсами, такими как память, дескрипторы файлов, сокеты, дескрипторы устройств и т. Д., И все эти ресурсы распределяются между потоками ядра.

Планировщик операционной системы является частью ядра, которое запускает каждый поток в течение определенного времени (на однопроцессорной машине). Планировщик распределяет время (временное сечение) каждому потоку, и если поток не завершается в течение этого времени, планировщик опережает его (прерывает его и переключается на другой поток). Несколько потоков могут работать параллельно на многопроцессорной машине, так как каждый поток может (но не обязательно) планироваться на отдельный процессор.

На однопроцессорной машине потоки разделены по времени и прерваны (переключены между) быстро (в Linux временной интервал по умолчанию равен 100 мс), что делает их параллельными. Тем не менее, они не могут быть запущены параллельно (одновременно), поскольку одноядерный процессор может запускать только одну вещь за раз.

Сопрограммы и / или генераторы могут использоваться для реализации совместных функций. Вместо того, чтобы запускаться в потоках ядра и планироваться операционной системой, они работают в одном потоке до тех пор, пока не завершат работу или не завершат работу, уступая другим функциям, определенным программистом. Языки с генераторами , такие как Python и ECMAScript 6, могут использоваться для создания сопрограмм. Async / await (замечено в C #, Python, ECMAscript 7, Rust) - это абстракция, построенная поверх функций генератора, которые дают будущее / обещание.

В некоторых контекстах сопрограммы могут ссылаться на стековые функции, в то время как генераторы могут ссылаться на функции без стеков.

Волокна , легкие нити и зеленые нити - это другие названия сопрограмм или подобных им вещей. Иногда они могут выглядеть (как правило, специально) как потоки операционной системы в языке программирования, но они не работают параллельно, как реальные потоки, а вместо этого работают как сопрограммы. (Могут быть более конкретные технические особенности или различия между этими понятиями в зависимости от языка или реализации.)

Например, у Java были « зеленые нити »; это были потоки, которые были запланированы виртуальной машиной Java (JVM), а не изначально в потоках ядра базовой операционной системы. Они не работают параллельно и не используют преимущества нескольких процессоров / ядер - поскольку для этого потребуется собственный поток! Поскольку они не были запланированы ОС, они были больше похожи на сопрограммы, чем на потоки ядра. Зеленые потоки - это то, что использовала Java до тех пор, пока нативные потоки не были введены в Java 1.2.

Потоки потребляют ресурсы. В JVM каждый поток имеет свой собственный стек, обычно размером 1 МБ. 64 КБ - это наименьшее количество стекового пространства, разрешенное для каждого потока в JVM. Размер стека потока можно настроить в командной строке для JVM. Несмотря на название, потоки не являются свободными, поскольку их ресурсам использования, таким как каждому потоку, требуется свой собственный стек, локальное хранилище потоков (если оно есть) и стоимость планирования потоков / переключения контекста / аннулирования кэша ЦП. Это одна из причин того, почему сопрограммы стали популярными для приложений, критичных к производительности, с высокой степенью одновременности.

Mac OS позволяет процессу выделять только около 2000 потоков, а Linux выделяет стек по 8 МБ на поток и разрешает только столько потоков, сколько умещается в физической памяти.

Следовательно, потоки имеют наибольший вес (с точки зрения использования памяти и времени переключения контекста), затем сопрограммы и, наконец, генераторы - наименьший вес.

llambda
источник
2
+1, но этот ответ может быть полезным из некоторых ссылок.
Кодзиро
1
Зеленые нити - это нечто отличное от сопрограмм. не так ли? Даже волокна имеют некоторые различия. см programmers.stackexchange.com/questions/254140/...
113

Примерно на 7 лет позже, но в ответах здесь не хватает некоторого контекста в сопрограммах против потоков. Почему сопрограммам уделяется так много внимания в последнее время, и когда я буду использовать их по сравнению с потоками ?

Прежде всего, если сопрограммы работают одновременно (никогда не параллельно ), почему кто-то предпочел бы их потокам?

Ответ заключается в том, что сопрограммы могут обеспечить очень высокий уровень параллелизма с минимальными издержками . Как правило, в многопоточной среде у вас есть не более 30-50 потоков, прежде чем количество потраченных накладных расходов на фактическое планирование этих потоков (системным планировщиком) значительно сокращает время, которое потоки фактически выполняют полезную работу.

Итак, с потоками вы можете иметь параллелизм, но не слишком большой параллелизм, разве это не лучше, чем совместная подпрограмма, выполняемая в одном потоке? Ну, не обязательно. Помните, что подпрограмма может по-прежнему выполнять параллелизм без затрат на планировщик - она ​​просто сама управляет переключением контекста.

Например, если у вас есть подпрограмма, выполняющая некоторую работу, и она выполняет операцию, которую, как вы знаете, заблокирует на некоторое время (например, сетевой запрос), с помощью подпрограммы вы можете немедленно переключиться на другую подпрограмму без дополнительных затрат на включение системного планировщика в это решение - да вы программист должны указать, когда сопрограммы могут переключаться.

С большим количеством подпрограмм, выполняющих очень небольшие работы и добровольно переключающихся между собой, вы достигли уровня эффективности, на который никогда не мог рассчитывать планировщик. Теперь вы можете иметь тысячи сопрограмм, работающих вместе, а не десятки потоков.

Поскольку ваши подпрограммы теперь переключаются между собой заранее определенными точками, теперь вы также можете избежать блокировки общих структур данных (потому что вы никогда не скажете своему коду переключаться на другую сопрограмму в середине критической секции)

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

И, наконец, сопрограммам уделяется много внимания, потому что в некоторых языках программирования (таких как Python) ваши потоки не могут работать параллельно в любом случае - они работают одновременно, как сопрограммы, но без недостатка памяти и свободных затрат на планирование.

Мартин Конечни
источник
2
Как сделать переключение на другую задачу в сопрограммах, когда мы сталкиваемся с операцией блокировки?
Нарцисс Дудье Сиве
Чтобы переключиться на другую задачу, нужно, чтобы любая операция блокировки выполнялась асинхронно. Это означает, что вы должны избегать использования каких-либо операций, которые фактически блокируют, и использовать только те операции, которые поддерживают неблокирование при использовании в вашей системе сопрограмм. Единственный способ обойти это - иметь сопрограммы, поддерживаемые ядром, например UMS в Windows, например, когда они переходят в ваш планировщик всякий раз, когда ваша UMS «нить» блокирует системный вызов.
retep998
@MartinKonecny ​​Является ли недавняя версия C ++ Threads TS подходом, который вы упомянули?
Никос
Таким образом, в конечном счете, современному языку программирования потребуются оба сопрограммы / волокна, чтобы эффективно использовать одно ядро ​​ЦП, например, для операций, не требующих большого объема вычислений, таких как ввод-вывод и потоки, для распараллеливания интенсивных операций ЦП на многих ядрах для увеличения скорости, верно?
Mahatma_Fatal_Error
19

Одним словом: упреждение. Сопрограммы действуют как жонглеры, которые продолжают передавать друг другу хорошо отрепетированные очки. Потоки (истинные потоки) могут быть прерваны практически в любой точке, а затем возобновлены позже. Конечно, это приводит к возникновению всевозможных проблем с конфликтом ресурсов, отсюда и печально известный GIL - Global Interpreter Lock.

Многие реализации потоков на самом деле больше похожи на сопрограммы.

Питер Роуэлл
источник
9

Это зависит от языка, который вы используете. Например, в Lua это одно и то же (называется тип переменной сопрограммы thread).

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

Вместо этого потоки автоматически управляются (останавливаются и запускаются) ОС, и они могут одновременно выполняться на многоядерных процессорах.

Томас Бонини
источник
0

12 лет опаздывают на обсуждение, но у сопрограммы есть объяснение в названии. Сопрограмма может быть разложена на Co и рутин.

Подпрограмма в этом контексте - это просто последовательность операций / действий, и при выполнении / обработке подпрограммы последовательность операций выполняется одна за другой в том же порядке, как указано.

Ко выступает за сотрудничество. Подпрограмму просят (или лучше ожидать) добровольно приостановить ее выполнение, чтобы дать другим подпрограммам возможность выполнить тоже. Таким образом, подпрограмма заключается в совместном использовании ресурсов ЦП, чтобы другие могли использовать тот же ресурс, что и сам.

Поток с другой стороны не должен приостанавливать свое выполнение. Приостановка полностью прозрачна для потока, и нижележащее оборудование заставляет поток приостановить себя. Это также делается таким образом, чтобы он был в основном прозрачным для потока, поскольку он не получает уведомления, и его состояние не изменяется, но сохраняется и впоследствии восстанавливается, когда потоку разрешено продолжать.

Одна вещь, которая не соответствует действительности, это то, что совместные процедуры не могут быть выполнены одновременно, и условия гонки не могут возникнуть. Это зависит от системы, в которой выполняются сопрограммы, и это легко возможно для создания сопрограмм.

Неважно, как сопрограммы приостановить себя. В Windows 3.1 int 03 вплетался в любые программы (или должен был быть там помещен), а в C # мы добавляем yield.

Мартин Керстен
источник