Сопрограмма

Эта статья находится на начальном уровне проработки, в одной из её версий выборочно используется текст из источника, распространяемого под свободной лицензией
Материал из энциклопедии Руниверсалис

Сопрограмма (англ. coroutine) — программный модуль, особым образом организованный для обеспечения взаимодействия с другими модулями по принципу кооперативной многозадачности: модуль приостанавливается в определённой точке, сохраняя полное состояние (включая стек вызовов и счётчик команд), и передаёт управление другому, тот, в свою очередь, выполняет задачу и передаёт управление обратно, сохраняя свои стек и счётчик. Наряду с фиберами (англ. fiber), сопрограммы являются средством обеспечения «легковесной» программной многопоточности в том смысле, что могут быть реализованы без использования механизмов переключений контекста операционной системой.

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

Появление понятия сопрограммы относят к конструкции, применённой Мелвином Конвеем[en] в 1958 году в практике программирования на языке ассемблера[1], в 1960-е — 1970-е годы сопрограммы практиковались в некоторых высокоуровневых языках (Клу, Симула, Модула-2), но заметное распространение получили лишь в 2000-е годы, когда появились многочисленные библиотеки поддержки сопрограмм в популярных языках программирования, а в некоторые новые языки (такие как Lua, Ruby, Go, Julia) встроены изначально. Сопрограммы используются для реализации многих похожих компонентов программ, таких как генераторы и итераторы, бесконечные списки с использованием ленивых вычислений, каналы, конечные автоматы внутри одной подпрограммы (где состояние определяется по текущей точке входа и выхода), реализации обработки исключений и модели акторов.

Реализации

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

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

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

Некоторые попытки реализовать сопрограммы на Си:

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

Функциональные языки программирования зачастую реализуют сопрограммы, например, Scheme, Лисп, Haskell. В ряд языков встроенная поддержка сопрограмм добавлена в последующих реализациях, таковы, например, Python (начиная с 2.5 и с явной синтаксической поддержкой, начиная с 3.5), PHP (начиная с 5.5), Kotlin (с версии 1.1), JavaScript (с версии 1.7), C# (с версии 2.0), Tcl (с версии 8.6).

Примечания

  1. Melvin E. Conway. Design of a separable transition-diagram compiler // Communications of the ACM. — 1963. — Т. 6, № 7. — С. 396–408. — doi:10.1145/366663.366704.
  2. Coroutines in C. Дата обращения: 17 марта 2006. Архивировано 9 ноября 2019 года.
  3. Portable Coroutine Library Home Page. Дата обращения: 17 марта 2006. Архивировано 14 декабря 2005 года.
  4. Index of /~froese/coro

Литература

  • Дональд Кнут. 1.4.2: Сопрограммы // Искусство программирования, том 1. Основные алгоритмы = The Art of Computer Programming, vol.1. Fundamental Algorithms. — 3-е изд. — М.: «Вильямс», 2006. — С. 229—236. — 720 с. — ISBN 0-201-89683-4.
  • Роберт Лав. Сопрограммы и фиберы // Linux. Системное программирование. — 2-е изд.. — СПб.: Питер, 2014. — С. 261. — 448 с. — ISBN 978-5-496-00747-4.