Сопрограмма


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

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

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

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

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