Что такое сопрограмма?
Что такое сопрограмма? Как они связаны с параллелизмом?
15 ответов
Сопрограммы и параллелизм в значительной степени ортогональны. Сопрограммы - это общая структура управления, посредством которой управление потоком совместно передается между двумя различными процедурами без возврата.
Оператор yield в Python является хорошим примером. Это создает сопрограмму. Когда встречается "yield", текущее состояние функции сохраняется, и управление возвращается вызывающей функции. Вызывающая функция может затем передать выполнение обратно в функцию-получатель, и ее состояние будет восстановлено до точки, где был обнаружен "yield", и выполнение будет продолжено.
Я нахожу большинство ответов слишком техническими, хотя это технический вопрос. Мне было трудно понять процесс сопрограммы. Я вроде получаю это, но тогда я не получаю это в то же время.
Я нашел этот ответ здесь очень полезным:
https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9
Цитировать от Идана Арье:
Чтобы развить вашу историю, я бы сказал так:
Вы начинаете смотреть мультфильм, но это вступление. Вместо того, чтобы смотреть вступление, вы переключаетесь в игру и заходите в онлайн-лобби - но для этого нужны 3 игрока, и в нем участвуют только вы и ваша сестра. Вместо того, чтобы ждать, пока другой игрок присоединится к вам, переключитесь на домашнюю работу и ответьте на первый вопрос. Второй вопрос имеет ссылку на видео YouTube, которое нужно посмотреть. Вы открываете его - и он начинает загружаться. Вместо того, чтобы ждать загрузки, вы переключаетесь обратно на мультфильм. Вступление окончено, так что вы можете посмотреть. Теперь есть реклама - но тем временем третий игрок присоединился, чтобы вы переключились на игру И так далее...
Идея состоит в том, что вы не просто переключаете задачи очень быстро, чтобы создать впечатление, что вы делаете все сразу. Вы используете время, которое вы ждете, чтобы что-то произошло (IO), чтобы делать другие вещи, которые требуют вашего непосредственного внимания.
Обязательно проверьте ссылку, там гораздо больше, что я не могу все процитировать.
Из программирования в Lua " Coroutines
" раздел:
Сопрограмма похожа на поток (в смысле многопоточности): это строка выполнения, со своим собственным стеком, своими локальными переменными и собственным указателем инструкций; но он разделяет глобальные переменные и в основном все остальное с другими сопрограммами. Основное различие между потоками и сопрограммами заключается в том, что концептуально (или буквально в многопроцессорной машине) программа с потоками запускает несколько потоков параллельно. Сопрограммы, с другой стороны, являются совместными: в любой момент времени программа с сопрограммами выполняет только одну из своих сопрограмм, и эта запущенная сопрограмма приостанавливает свое выполнение только тогда, когда она явно запрашивает приостановку.
Итак, суть в том, что сопрограммы являются "совместными". Даже в многоядерной системе в каждый момент времени работает только одна сопрограмма (но несколько потоков могут работать параллельно). Между сопрограммами нет преимущественных прав: запущенная сопрограмма должна явно отказаться от выполнения.
За " concurrency
"Вы можете сослаться на слайд Роба Пайка:
Параллельность - это композиция независимо выполняемых вычислений.
Таким образом, во время выполнения сопрограммы A она передает управление сопрограмме B. Затем через некоторое время сопрограмма B передает управление обратно сопрограмме A. Поскольку существует зависимость между сопрограммами, и они должны работать в тандеме, поэтому две сопрограммы не являются параллельными.
Я считаю, что объяснение по этой ссылке довольно простое. Ни один из этих ответов не пытается объяснить параллелизм и параллелизм, за исключением последнего пункта в этом ответе.
- что такое параллельная (программа)?
цитируется из "программирования на Erlang" Джо Армстронга, легендарного:
параллельная программа может работать потенциально быстрее на параллельном компьютере.
параллельная программа - это программа, написанная на языке параллельного программирования. Мы пишем параллельные программы из соображений производительности, масштабируемости или отказоустойчивости.
язык параллельного программирования - это язык, который имеет явные языковые конструкции для написания параллельных программ. Эти конструкции являются неотъемлемой частью языка программирования и ведут себя одинаково во всех операционных системах.
параллельный компьютер - это компьютер, который имеет несколько процессоров (процессоров или ядер), которые могут работать одновременно.
Таким образом, параллелизм - это не то же самое, что параллелизм. Вы по-прежнему можете писать параллельные программы на одноядерном компьютере. Планировщик с разделением времени заставит вас почувствовать, что ваша программа работает одновременно.
Параллельная программа может работать параллельно на параллельном компьютере, но это не гарантируется. ОС может дать вам только одно ядро для запуска вашей программы.
Следовательно, параллелизм - это программная модель из параллельной программы, которая не означает, что ваша программа может работать параллельно физически.
- сопрограмма и параллелизм
Слово "coroutine" состоит из двух слов: "co" (кооперативный) и "routines" (функции).
а. достигается ли параллелизм или параллелизм?
Для простоты давайте обсудим это на одноядерном компьютере.
Параллелизм достигается за счет таймшера из ОС. Поток выполняет свой код в назначенные ему временные рамки на ядре ЦП. Может быть выгружен ОС. Это также может передать управление ОС.
С другой стороны, сопрограмма передает управление другой сопрограмме в потоке, а не ОС. Таким образом, все сопрограммы в потоке по-прежнему используют временные рамки для этого потока, не уступая ядро ЦП другим потокам, управляемым ОС.
Следовательно, вы можете подумать, что сопрограмма обеспечивает разделение времени пользователем, а не ОС (или квазипараллелизмом). Сопрограммы выполняются на том же ядре, которое назначено потоку, который запускает эти сопрограммы.
Достигает ли Coroutine параллелизма? Если это код, привязанный к процессору, нет. Как и в случае с разделением времени, вы чувствуете, что они работают параллельно, но их выполнение чередуется, а не перекрывается. Если он привязан к вводу-выводу, да, он достигает параллельности аппаратно (устройства ввода-вывода), а не вашим кодом.
б. разница с вызовом функции?
Как видно на картинке, звонить не нужно return
переключить управление. Он может уступить безreturn
. Сопрограмма сохраняет и передает состояние в текущем функциональном фрейме (стеке). Таким образом, он намного легче, чем функция, поскольку вам не нужно сохранять регистры и локальные переменные в стек и перематывать стек вызовов, когдаcall ret
.
Сопрограмма похожа на подпрограмму / темы. Разница заключается в том, что когда вызывающая сторона вызывает подпрограмму / потоки, она никогда не вернется к функции вызывающей стороны. Но сопрограмма может вернуться обратно к вызывающей стороне после выполнения нескольких частей кода, позволяя вызывающей стороне выполнить часть своего собственного кода и вернуться к точке сопрограммы, где она остановила выполнение и продолжила оттуда. то есть. Сопрограмма имеет более одной точки входа и выхода
- Сопрограммы - это отличные возможности, доступные на Kotlin Language
- Сопрограммы - это новый способ написания асинхронного неблокирующего кода (и многое другое)
- Coroutine - легкие нити. Облегченный поток означает, что он не отображается на собственный поток, поэтому он не требует переключения контекста на процессоре, поэтому они быстрее.
- он не отображается на нить
- Сопрограммы и потоки являются многозадачными. Но разница в том, что потоки управляются операционной системой, а сопрограммы - пользователями.
В основном, есть два типа сопрограмм:
- Stackless
- Stackful
Kotlin реализует сопрограммы без стеков - это означает, что сопрограммы не имеют собственного стека, поэтому они не отображаются в нативный поток.
Это функции для запуска сопрограммы:
launch{}
async{}
Вы можете узнать больше здесь:
https://www.kotlindevelopment.com/deep-dive-coroutines/
https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9
Если вы все еще запутались, вот очень простой способ понять файл . Прежде всего, что такое? С точки зрения непрофессионала, рутина — это то, что мы делаем снова и снова (например, ваша утренняя рутина). Сходным образом. в языках программирования,
routine
это фрагмент кода, который мы используем снова и снова, например,
a function
. Теперь, если вы посмотрите на общую характеристику
function or routine
(примечание: я осторожно использую эти два термина как взаимозаменяемые), он принимает некоторые входные данные и загружает потоки ЦП до тех пор, пока функция должна выводить результат. Значение,
functions or routines
блокируют вызовы в вашем коде. Однако
co-routine
это особый вид подпрограммы, которая может сосуществовать (от этого слова происходит часть слова co-routine) с другими подпрограммами одновременно, и мы можем сделать это в языках программирования с помощью асинхронного программирования. В асинхронном программировании, когда одна сопрограмма ожидает, что что-то произойдет (например, дисковый ввод-вывод), другая сопрограмма начнет работать, и когда эта сопрограмма находится в состоянии ожидания, другая сопрограмма будет активна в конечном итоге. сокращение времени ожидания нашего кода.
Если вы понимаете вышеизложенное, давайте посмотрим, как вы можете создать функцию сопрограммы в Python. Вы можете определить сопрограммную функцию следующим образом:
async def my_coroutine_function():
return 123
И вы можете вызвать вышеуказанную сопрограмму, добавив
await
перед сопрограммой-
my_result = await my_coroutine_function()
Заключить,
Когда вы смотрите телепередачу и как только приходит реклама, вы берете свой телефон и пишете сообщение другу — то, что вы только что сделали, — это асинхронное программирование. Пока ваше телешоу (сопрограмма) находилось в состоянии ожидания, вы активировали другую сопрограмму (сообщение другу).
Корутина как реализация параллелизма и альтернатива многопоточности.
Сопрограмма — это однопоточное решение для достижения параллелизма.
A-Start ------------------------------------------ A-End
| B-Start -----------------------------------------|--- B-End
| | C-Start ------------------- C-End | |
| | | | | |
V V V V V V
1 thread->|<-A-|<--B---|<-C-|-A-|-C-|--A--|-B-|--C-->|---A---->|--B-->|
По сравнению с многопоточным решением:
thread A->|<--A| |--A-->|
thread B------>|<--B| |--B-->|
thread C ---------->|<---C| |C--->|
- Coroutine — это реализация асинхронного программирования, а асинхронное программирование используется для реализации параллелизма.
- Coroutine — это асинхронное программирование, а все асинхронное программирование неблокирующее.
- Многие языки реализуют асинхронное программирование с сопрограммами. Другие ответы предполагают, что Python, Kotlin, Lua, C++ сделали это.
- Наиболее полезно/обычно используется в сценариях, связанных с проблемами, связанными с вводом-выводом, например, отрисовка пользовательского интерфейса при выборке данных или загрузка из нескольких источников данных.
Сопрограмма - это особый вид подпрограммы. В отличие от отношений главный-подчиненный между вызывающим и вызываемой подпрограммой, которые существуют с обычными подпрограммами, вызывающий и вызываемые сопрограммы более справедливы.
Сопрограмма - это подпрограмма, которая имеет несколько записей и сама контролирует их - поддерживается непосредственно в Lua.
Также называется симметричным управлением: вызывающий и вызываемые сопрограммы находятся на более равной основе.
Вызов сопрограммы называется резюме
Первое резюме сопрограммы находится в ее начале, но последующие вызовы входят в точку сразу после последнего выполненного оператора в сопрограмме.
Сопрограммы многократно возобновляют друг друга, возможно, навсегда
Сопрограммы обеспечивают квази-параллельное выполнение программных модулей (сопрограмм); их выполнение чередуется, но не перекрывается
С другой стороны, в Python gevent
библиотека coroutine
основанная сетевая библиотека, которая предоставляет вам нитевидные функции, такие как асинхронные сетевые запросы, без дополнительных затрат на создание и уничтожение потоков. coroutine
используемая библиотека greenlet
,
Из Python Coroutine:
Выполнение сопрограмм Python может быть приостановлено и возобновлено во многих точках (см. Сопрограмму). Внутри тела функции сопрограммы идентификаторы await и async становятся зарезервированными ключевыми словами; выражения await, async for и async with могут использоваться только в телах функций сопрограмм.
Сопрограмма - это функция, которая может приостановить выполнение, чтобы быть возобновленной позже. Сопрограммы без стеков: они приостанавливают выполнение, возвращаясь к вызывающей стороне. Это учитывает последовательный код, который выполняется асинхронно (например, для обработки неблокирующего ввода-вывода без явных обратных вызовов), а также поддерживает алгоритмы для вычислений с отложенным вычислением бесконечных последовательностей и других целей.
Сравните с ответом другого:
На мой взгляд, возобновленная более поздняя часть - это принципиальное отличие, как у @Twinkle's.
Хотя многие поля документа все еще находятся в стадии разработки, тем не менее, эта часть похожа на большинство ответов, за исключением @Nan Xiao 's
Сопрограммы, с другой стороны, являются совместными: в любой момент времени программа с сопрограммами выполняет только одну из своих сопрограмм, и эта запущенная сопрограмма приостанавливает свое выполнение только тогда, когда она явно запрашивает приостановку.
Поскольку он цитируется в программе на Lua, возможно, он связан с языком (в настоящее время не знаком с Lua), не во всех документах упоминается только одна часть.
Отношение с одновременным:
Есть часть "Выполнение" сопрограмм (C++20). Слишком долго, чтобы цитировать здесь.
Помимо деталей, есть несколько состояний.
When a coroutine begins execution
When a coroutine reaches a suspension point
When a coroutine reaches the co_return statement
If the coroutine ends with an uncaught exception
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle
как комментарий @Adam Arold под ответом @user217714. Это параллелизм.
Но это отличается от многопоточности. из std::thread
Потоки позволяют выполнять несколько функций одновременно. Потоки начинают выполнение сразу же после создания связанного объекта потока (в ожидании любых задержек планирования ОС), начиная с функции верхнего уровня, предоставленной в качестве аргумента конструктора. Возвращаемое значение функции верхнего уровня игнорируется, и, если оно завершается выдачей исключения, вызывается std::terminate. Функция верхнего уровня может передавать свое возвращаемое значение или исключение вызывающей стороне через std:: обещание или путем изменения общих переменных (что может потребовать синхронизации, см. Std::mutex и std::atomic)
Поскольку это параллелизм, он работает как многопоточность, особенно когда ожидание неизбежно (с точки зрения ОС), поэтому он также сбивает с толку.
Я расширю ответ @user21714. Сопрограммы - это независимые пути выполнения, которые не могут выполняться одновременно. Они зависят от контроллера - например,python
библиотека контроллера - для управления переключением между этими путями. Но для того, чтобы это сработало, сопрограммы должны вызыватьyield
или аналогичные структуры, позволяющие приостанавливать их выполнение.
Вместо этого потоки работают на независимых вычислительных ресурсах и параллельно друг другу. Так как они находятся на разных ресурсах, нет необходимости вызывать yield, чтобы разрешить другие пути выполнения.
Вы можете увидеть этот эффект, запустив многопоточную программу - например, jvm
приложение - в котором все восемь ваших core i7
используются ядра с гиперпоточностью: вы можете увидеть 797% загрузки в Activity Monitor
или Top
. Вместо этого при запуске типичногоpython
программа - даже одна с coroutines
или python threading
- загрузка будет максимально 100%. Т.е. одна машина гиперпотоковая.
[Синхронный против асинхронного]
[Параллелизм против параллелизма]
Обычно мы думаем что-то вроде: сопрограммы — это легкие потоки, они позволяют нам писать асинхронный, неблокирующий код синхронным образом.
Что касается сопрограмм Kotlin:
Coroutine — это синтетический сахар/дополнительный слой, который позволяет выполнять большую задачу неблокирующим образом и без обратных вызовов . Корутина состоит из нескольких классов (
Job
,
Dispatcher
,
Scope
,
Builder
) а также
body
Давайте рассмотрим пример
suspend fun downloadFile(): File {
//logic
}
suspend fun saveFile(file: File) {
//logic
}
GlobalScope.launch {
val downloadResult = downloadFile() //suspend function
show(downloadResult) //UI
saveFile(downloadResult) //suspend function
}
Он создает класс, который
state machine
с
invokeSuspend()
функция
class Continuation {
File file;
void invokeSuspend(Object result) {
switch (label) {
case 0: {
label = 1;
downloadFile(this); //suspend function
return;
}
case 1: {
file = (File) result;
show(file); //UI
saveFile(file, this); //suspend function
return;
}
}
}
}
приостановленный
- просто маркер для работы с
Continuation
- передает продолжение в функцию - делит конечный автомат, что означает, что он может приостановить машину
- следует использовать обратный вызов, внутри которого вызывается
Continuation.resume() -> Continuation.invokeSuspend()
Главное, что поведение
coroutine
полностью зависит от реализации библиотеки
По пути длинная улица с банками, офисами, магазинами, ресторанами, школами. У вас, ваших соседей, друзей, членов семьи и совершенно незнакомых людей — у всех вас, живущих на этой улице, есть работа.
Теперь вы начинаете из дома и берете такси. На полпути вам придется остановиться в банке, чтобы снять немного денег. Вы выходите из такси, потому что не хотите заставлять его ждать. Такси теперь может бесплатно подвезти кого-нибудь, у кого есть работа.
Как только вы закончите свою работу в банке, вы выходите, махаете другому такси, которое ждало неподалеку, и двигаетесь дальше.
Вы все — сопрограммы . Такси — это темы.
Из Википедии:
Сопрограммы очень похожи на потоки. Однако сопрограммы являются многозадачными совместно, тогда как потоки обычно являются многозадачными с вытеснением. Сопрограммы обеспечивают параллелизм, поскольку позволяют выполнять задачи вне последовательности или в изменяемом порядке, без изменения общего результата, но они не обеспечивают параллелизм, поскольку не выполняют несколько задач одновременно. Преимущества сопрограмм перед потоками заключаются в том, что их можно использовать в контексте жесткого реального времени (переключение между сопрограммами не требует каких-либо системных вызовов или каких-либо блокирующих вызовов), нет необходимости в примитивах синхронизации, таких как мьютексы, семафоры и т. д. для защиты критических разделов и не требует поддержки со стороны операционной системы.
Можно реализовать сопрограммы с использованием потоков с упреждающим планированием таким образом, чтобы это было прозрачно для вызывающего кода, но некоторые преимущества (в частности, пригодность для работы в жестком реальном времени и относительная дешевизна переключения между ними) будут потеряны.