Разница между подпрограммой, совместной процедурой, функцией и потоком?

Я читал искусство программирования, том 1, за последние 2 дня.

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

Я попытался исследовать ранее заданный вопрос здесь, где я познакомился с понятиями функций и потоков тоже в том же контексте В книге приведен пример совместного подхода с использованием компьютерной модели MIX. Могу ли я получить какой-либо простой псевдокод, чтобы я понял, используя пример о совлокальных подпрограммах и других терминологиях в контексте языков высокого уровня?

Итак, совлокальный вопрос, как различать подпрограмму, совместную процедуру, функцию и поток?

Ответ 1

Пока мы не получаем сообщение от кого-то, кто действительно знает, вот мое понимание вопроса, FWIW.

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

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

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

Назад к совместному подпрограмме: как упоминалось ранее, у него есть одна или несколько точек повторного входа. Точка повторного входа означает, что совместная подпрограмма может позволить некоторому другому блоку кода за пределами себя иметь некоторое время выполнения, а затем в какое-то время будет возвращать время выполнения в своем собственном блоке кода. Это означает, что параметры и автоматические переменные co-подпрограммы сохраняются (и восстанавливаются, если необходимо), когда выполнение выполняется во внешний блок кода, а затем возвращается к кодовому подпрограмме. Совместная подпрограмма - это то, что непосредственно не реализовано на каждом языке программирования, хотя оно является общим для многих языков ассемблера. Во всяком случае, концептуально можно реализовать совместную процедуру. Существует хорошая статья о совлокальных процедурах на http://en.wikipedia.org/wiki/Coroutine.

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

НТН

Ответ 2

Фокусировка на сопрограмме coroutine против подпрограммы:

Coroutines может уступить, и это интересно.

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

Например:

  coroutine foo {
    yield 1;
    yield 2;
    yield 3;
  }
  print foo();
  print foo();
  print foo();

Печать: 1 2 3

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

  coroutine foo {
    return 1;
    return 2; //Dead code
    return 3;
  }
  print foo();
  print foo();
  print foo();

Печать: 1 1 1

Ответ 3

Я хотел бы расширить существующие ответы, добавив следующее:
Существует четыре основных понятия при вызове фрагмента кода:

  • создание контекста (aka "frame", локальная среда для работы этого кода)
  • возобновление/вызов контекста (передача управления в этот фрейм, ака прыжок)
  • отключение (возобновление другого контекста, похожего на 2.)
  • и уничтожение контекста

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

Ответ 4

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

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