Что такое сопрограмма?

Что такое сопрограмма? Как они связаны с параллелизмом?

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. Поскольку существует зависимость между сопрограммами, и они должны работать в тандеме, поэтому две сопрограммы не являются параллельными.

Я считаю, что объяснение по этой ссылке довольно простое. Ни один из этих ответов не пытается объяснить параллелизм и параллелизм, за исключением последнего пункта в этом ответе.

  1. что такое параллельная (программа)?

цитируется из "программирования на Erlang" Джо Армстронга, легендарного:

параллельная программа может работать потенциально быстрее на параллельном компьютере.

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

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

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

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

Параллельная программа может работать параллельно на параллельном компьютере, но это не гарантируется. ОС может дать вам только одно ядро ​​для запуска вашей программы.

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

  1. сопрограмма и параллелизм

Слово "coroutine" состоит из двух слов: "co" (кооперативный) и "routines" (функции).

а. достигается ли параллелизм или параллелизм?

Для простоты давайте обсудим это на одноядерном компьютере.

Параллелизм достигается за счет таймшера из ОС. Поток выполняет свой код в назначенные ему временные рамки на ядре ЦП. Может быть выгружен ОС. Это также может передать управление ОС.

С другой стороны, сопрограмма передает управление другой сопрограмме в потоке, а не ОС. Таким образом, все сопрограммы в потоке по-прежнему используют временные рамки для этого потока, не уступая ядро ​​ЦП другим потокам, управляемым ОС.

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

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

б. разница с вызовом функции?

Как видно на картинке, звонить не нужно returnпереключить управление. Он может уступить безreturn. Сопрограмма сохраняет и передает состояние в текущем функциональном фрейме (стеке). Таким образом, он намного легче, чем функция, поскольку вам не нужно сохранять регистры и локальные переменные в стек и перематывать стек вызовов, когдаcall ret.

Сопрограмма похожа на подпрограмму / темы. Разница заключается в том, что когда вызывающая сторона вызывает подпрограмму / потоки, она никогда не вернется к функции вызывающей стороны. Но сопрограмма может вернуться обратно к вызывающей стороне после выполнения нескольких частей кода, позволяя вызывающей стороне выполнить часть своего собственного кода и вернуться к точке сопрограммы, где она остановила выполнение и продолжила оттуда. то есть. Сопрограмма имеет более одной точки входа и выхода

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

В основном, есть два типа сопрограмм:

  1. Stackless
  2. 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 могут использоваться только в телах функций сопрограмм.

Из сопрограмм (C++20)

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

Сравните с ответом другого:

На мой взгляд, возобновленная более поздняя часть - это принципиальное отличие, как у @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полностью зависит от реализации библиотеки

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

Теперь вы начинаете из дома и берете такси. На полпути вам придется остановиться в банке, чтобы снять немного денег. Вы выходите из такси, потому что не хотите заставлять его ждать. Такси теперь может бесплатно подвезти кого-нибудь, у кого есть работа.

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

Вы все — сопрограммы . Такси — это темы.

Из Википедии:

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

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

Другие вопросы по тегам