Привет, Хабр! Меня зовут Дмитрий Попов, я Android‑разработчик в ПСБ. В какой‑то момент мне захотелось разобраться — что такое корутина и откуда они вообще взялись. Погрузившись в различные статьи и видео, я узнал много нового, рассказал коллегам внутри команды, а теперь решил рассказать и вам. Все, что важно знать о корутинах, по моему скромному мнению, — в этой статье!
Глава 1. Что такое корутина?
И начнем с определения.
На сегодняшний день корутины являются неотъемлемой частью современной разработки под Android и не только. Они позволяют писать асинхронный код без вложенных колбэков, управлять выполнением, удобно обрабатывать ошибки и создавать большое количество корутин без существенной нагрузки.
В соответствии с определением из Википедии, корутина представляет собой средство обеспечения «легковесной» многопоточности — т.е., без переключения контекста между тяжеловесными потоками операционной системы.
Как известно, потоки создаются ОС и выполняются на нескольких ядрах процессора. При создании потока выделяется память под стек потока (+ кэш), а ядро (kernel) используется для переключения между потоками. При переключении, стек 1-го сериализуется и переносится в Heap, а стек 2-го потока извлекается из Heap и десериализуется. Это дорогая операция. Таким образом, потоки мощные, но тяжеловесные — как правило каждый поток требует несколько мегабайт памяти и JVM может запустить параллельно всего несколько тысяч потоков.
Параллельное и конкурентное выполнение

В классическом представлении, программа может работать на нескольких параллельных потоках, когда участки кода или целые модули выполняются в независимых друг от друга потоках.
В свою очередь, существует такое понятие, как «конкурентное» выполнение в рамках одного потока, когда части программы находятся в очереди за процессорное время с определенным приоритетом и выполняются в рамках одного потока – тогда и происходит переключение контекста.
Данные концепции можно объединить и тогда код может выполняться как параллельно, так и «конкурентно». И тут мы приходим к тому, как работают корутины, а именно - множество корутин выполняются в одном или нескольких потоках, но без существенных затрат на тяжеловесное переключение контекста между корутинами.
От потоков к корутинам

Корутины это так называемые «отложенные» вычисления. Благодаря Dispatchers они работают на пуле потоков управляемых ОС. Корутина может остановить свое выполнение, не блокируя поток, позволяя другим корутинам выполняться и экономя ресурсы.
Например, для запуска 50 тысяч потоков потребуется около 10 Гб, а для такого же количества корутин — в 20 раз меньше памяти (500 Мб).
Глава 2. Происхождение корутин
«Организации, проектирующие системы, ограничены дизайном, который копирует структуру коммуникации в этой организации».
М. Конвей
Закон Конвея
Корутины придумал Мелвин Конвей — американский ученый в области компьютерных наук, программист и теоретик систем, известный благодаря сформулированному им «закону Конвея» — его вы видели выше.
Он получил степени бакалавра и магистра по физике, а затем степень доктора математики. Его карьера в ИТ началась в 1956 году с работы на компьютерах с вакуумными лампами и перфокартами.
Именно Конвей разработал концепцию сопрограмм (корутин) и был первым, кто применил ее в ассемблере. Он также участвовал в разработке компиляторов COBOL и Macintosh Pascal для Apple.
На протяжении своей 50-летней карьеры Конвей стремился сделать процесс разработки ПО более доступным. Он выделял 3 этапа, или даже скорее урока, в своей работе.
Этап № 1: Инструменты разработки с быстрой «обратной связью»
Конвей проводил исследования, целью которых была разработка быстрых компиляторов.

1. Однопроходный компилятор COBOL (1963)
В 1963 году он опубликовал две влиятельные работы, описывающие архитектуру компилятора COBOL, который обрабатывал код за единичный проход. Данный компилятор использовался для компьютеров Burroughs B200/B300 — возможно, самой маленькой машины в истории на тот момент, на которой удалось запустить полноценный COBOL, что доказало: эффективный дизайн может компенсировать жесткие ограничения «железа».
2. Обучающая система Pascal для Rockwell AIM-65
Также Конвей создал тренажер для среды Pascal с «мгновенным откликом», который устанавливали в компьютеры Apple II. Данный тренажер стирал грань между написанием и запуском кода — позволял выполнять пошаговую отладку прямо на уровне исходного текста, благодаря чему переход от кода к выполнению казался мгновенным.
3. Сотрудничество с Apple и Think Technologies
Исследования Конвея в области мгновенной обратной связи привели к партнерству между его компанией Think Technologies и Apple. В результате были созданы несколько инструментов для обучения разработки для первых Macintosh, обладавших принципом «мгновенности». В результате пользователь не видел «швов» внутреннего языка исполнения — код просто работал сразу после написания, что было революционной концепцией для того времени.
Отсюда следует первый урок: инструменты разработки должны обеспечивать мгновенный отклик, что способствует творческому импульсу.
Этап № 2: Исследование «языков» приложений (ПО)

В середине 50-х годов массовые коммерческие задачи, такие как расчет платежей за коммунальные услуги, не «программировались» в привычном нам понимании. Вместо написания строк логики техники использовали перфокарты. Сами «алгоритмы» — механика того, как именно обрабатывались данные — уже были встроены в «железо» машин. Задача человека сводилась лишь к сопоставлению: обеспечению того, чтобы данные из определенного поля на карте корректно попадали в итоговый отчет.
Эта дало Конвею важное понимание, которое легло в основу его «Урока № 2» — продуктивный язык приложений не должен быть инструментом для описания алгоритмов; он должен стать оболочкой, которая скрывает их. Делая вопрос «как это работает» невидимым, язык освобождает программиста для полной концентрации на вопросе «что должно быть сделано.»
Этап № 3: Гуманизация процесса разработки ПО

Таким образом, основная проблема, выявленная Конвеем в разработке ПО — это «алгоритмический барьер». С самого рассвета компьютеров программирование было синонимом описания того, как машина должна выполнять задачу. Это требует специфического типа математической грамотности. Конвей считал, что необходимо сделать разработку ПО доступной для масс — требовалось устранить необходимость написания алгоритмов пользователями и сделать инструменты «гуманными». Иными словами, в программном обеспечении это означало, что инструмент должен обеспечивать опыт по принципу «что видишь, то и получаешь» (WYSIWYG‑редактор).
Конвей предвидел мир «разработки конечными пользователями», где инструмент настолько прозрачен, что пользователь даже не осознает, что он «программирует». Он просто «строит» или «компонует». Он считал, что подобная демократизация приведет к всплеску продуктивности и инноваций, поскольку люди, представляющие различные профессии, наконец‑то получат возможность создавать свои решения.
Первое упоминание термина «корутина»
В 1958 Мелвин Конвей работал над программой на ассемблере и придумал концепцию корутин. Идея была в том, чтобы обозначить блоки программ, которые взаимодействуют друг с другом посредством обмена информацией, в виде передачи дискретных значений, при этом работают независимо. То есть, такие блоки программы представляют собой сопрограммы (или корутины).
Первая публикация

В 1963 году Мелвин Конвей опубликовал статью с описанием программы и впервые официально ввел термин «корутина» в употребление. В качестве примера он рассказал про программу для чтения данных с перфокарты и дальнейшую их обработку.
Изначально программа в цикле считывала один символ с перфокарты, записывала на магнитную ленту и производила обработку. Тем самым происходила постоянная передача управления между разными небольшими операциями и программа выполнялась долго. Было решено разделить программу на 2 сопрограммы.
Под сопрограммой понимался некий модуль, который имел точку входа, выхода и мог работать отдельно от основной программы. Таким образом, одна сопрограмма считывала все данные с перфокарты и записывала их на ленту за один проход, после чего лента перематывалась в начало. Вторая сопрограмма считывала все данные с ленты и обрабатывала их целиком. Основная программа лишь передавала управление от первой сопрограммы ко второй. В итоге чтение, запись и обработка данных стали происходить гораздо быстрее.
В отличие от подпрограмм, идея корутин состоит в том, что каждая сопрограмма является независимой сущностью и может запоминать и восстанавливать свое состояние при передаче управления.
Глава 3. Спад интереса к корутинам
Несмотря на прорывную идею и активные исследования с 60-х по 80-е гг., корутины не получили широкого распространения ввиду отсутствия встроенной поддержки в популярных языках программирования и зрелых инструментов. Реализация концепции корутин была технически сложной и на низком уровне была сравнима с блокировкой и управлением потоками.
Альтернативы корутинам

В качестве альтернатив, использовались такие концепции как Threads, Callbacks и Promises/Futures. У каждого подхода свои достоинства и недостатки.
Например, базовые потоки поддерживаются во всех языках, но они тяжелые и требуют ресурсов для работы с ними напрямую.
Концепция Callbacks более легковесна, скрывает непосредственную работу с потоками, но приводит к такому понятию как callback hell.
Promises/Futures значительно упростили работу с асинхронным кодом, однако добавили лишнюю абстракцию в виде понятия «ожидание результата в будущем».
В свою очередь, корутины, благодаря suspend‑функциям, позволяют нам писать асинхронный код так, будто он синхронный.
Глава 4. Возрождение корутин
FTP‑сервер Simtel
В 80-х распространение программного обеспечения было непростой задачей. В основном использовались 3,5“ дискеты. Одновременно с этим стали появляться онлайн хранилища ПО, одним из которых была компания Simtel.
Компания начала свое существование в 1979 с хранения ПО на мейнфрейме PDP-10 в MIT. Однако, в 1983 мейнфрейм понадобился для одного из проектов университета, вследствие чего пришлось переехать на новый сервер DECSYSTEM-20, работавший в сети ARPANET, что дало возможность организовать доступ по FTP. В таком виде проект просуществовал до 1993 года, когда компания переехала на новое место и стала одним из самых популярных публичных FTP серверов того времени, просуществовав вплоть до 2013 года.


«Проблема C10k» (concurrent 10 000 connections)
Вернемся к корутинам. Особенно остро необходимость в снижении затрат на работу потоков возникла с появлением проблемы 10 тысяч одновременных соединений («проблема C10k»).
В 1999 году администратор FTP-сервера Simtel обратил внимание, что обслуживающий узел на гигабитном канале не справляется с нагрузкой, хотя по аппаратным показателям он должен был справляться с нагрузкой в 10 тысяч соединений. Причина оказалась в программном обеспечении, которое этого не позволяло. Это привело к появлению техник и серверов, способных справляться с подобными нагрузками.
В 2000-х потребность в асинхронном вводе/выводе и ожидании выполнения сетевых операций выросла еще больше.
А в 2010-х стали появляться решения, способные справляться с задачами куда большего масштаба, доходящими до 1 миллиона одновременных соединений. В 2020-х решения проблем подобного рода стали обозначать нумеронимом С10M – т.е., обеспечение порядка 10 миллионов соединений.
Современные ЦОД
Когда микросервисная архитектура стала набирать популярность и начался процесс постепенного ухода от монолита, микросервисы, работавшие с разной скоросью, стали общаться друг с другом, вследствие чего потребность в асинхронном вводе/выводе и ожидании выполнения сетевых операций возросла еще больше.
Нагрузка на серверы неуклонно повышалась.
Развитие интерфейсов в web- и мобильной разработке также привело к тому, что идея корутин вновь стала набирать популярность. Особенно важным было сократить все увеличивающуюся нагрузку, связанную с потоками (переключение контекста).
Глава 5. Широкое распространение
С развитием языков программирования и появлением необходимых библиотек, корутины стали удобным и эффективным инструментом. Давайте посмотрим на все факторы чуть пристальнее.
Развитие языков
Языки программирования, такие как Python, JavaScript и другие, начали добавлять поддержку корутин на уровне языка, что упростило их использование.
async/await
Появление синтаксиса async/await стало важным шагом, так как он позволил писать асинхронный код в более читаемом и последовательном стиле, имитируя синхронное программирование.
Библиотеки и фреймворки
С появлением библиотек и фреймворков для работы с корутинами, таких как asyncio в Python и Kotlin Coroutines, процесс их использования стал намного проще и удобнее.
Управление жизненным циклом
Современные системы для корутин включают механизмы для управления жизненным циклом сопрограмм, что делает их безопаснее и предсказуемее, чем в прошлом.
Вытесняющие / кооперирующие корутины

При этом в современном мире корутины делятся на 2 типа — вытесняющие и кооперирующие.
Вытеснение корутин (non‑cooperative) означает, что операционная система или среда выполнения может принудительно прерывать их выполнение.
Кооперация корутин (cooperative) означает, что корутины должны явно приостанавливать свое выполнение, чтобы передать управление другим с помощью ключевого слова suspend.
Преимущества кооперации:
Более простой и надежный код
Легче писать и отлаживать код, так как нет риска внезапного прерывания в любой моментStructured Concurrency
Позволяет управлять иерархией корутин через единый интерфейс. Это значит, что отмена родительской корутины автоматически отменяет все дочерние.
Например, такая реализация в Kotlin. Это дает возможность писать простой и надежный код, а также работать со Structured Concurrency.
Поддержка корутин в языках программирования
Тут стоит привести сравнительную таблицу между поддержкой концепции корутин в различных ЯП.

У всех реализаций есть свои преимущества, недостатки и особенности. Однако, в конечном итоге, все сводится к скорости работы и эффективности использования доступных ресурсов.
Современное состояние и тенденции
Современное состояние корутин таково, что они активно развиваются: появляются улучшения в библиотеках, новые dispatcher-ы, новые подходы к отмене, улучшения дебага и инструментов. Все больше внимания к тому, чтобы запуск корутины подразумевал ее явное завершение или отмену, чтобы избегать «утечек» корутин.
Применение в различных отраслях
Корутины кардинально изменили подходы не только в мобильной разработке, но и в разработке бэкенда, высоконагруженных серверных систем, GameDev, Data Science и даже системном программировании.
Важные вехи в развитии корутин
1958 – Мелвин Конвэй ввел термин «корутина» для оптимизации программы на ассемблере
1963 – Мелвин Конвэй опубликовал статью с описанием подхода корутин на примере программы чтения и записи данных с перфокарты на магнитную ленту
1960-е – 1970-е – активные исследования, эксперименты, симуляции
1980-е – публикации, развитие теории и затем
1980-е – 2000-е – спад интереса к корутинам, развитие подходов с использованием потоков и асинхронных колбэков
2000-е – возрождение интереса к корутинам для embedded, IoT, добавление в языки программирования и расширение поддержки
2010-е – стабилизация и широкое использование на уровне языков программирования, появление библиотек
2020-е – корутины используются во многих языках
Подводя итог, корутины прошли долгий путь от простой идеи, призванной оптимизировать выполнение программы, к повсеместному использованию и применению.
Не стоит забывать, что и у корутин есть свои минусы и в конечном итоге мы все равно физически ограничены железом на котором выполняется программа. Однако это еще один шаг в развитии и на какое-то время корутины должны решить возникшие перед нами вызовы.
На этом все, всем спасибо за то, что уделили время статье!
Полезные ссылки
Для более подробного ознакомления прилагаю ряд ссылок на материалы по корутинам.
Coroutines basics
https://kotlinlang.org/docs/coroutines-basics.htmlСтатья Мелвина Конвэя «Design of a Separable Transition-diagram Compiler»
https://melconway.com/Home/pdf/compiler.pdfМелвин Конвэй
https://en.wikipedia.org/wiki/Melvin_Conway
https://www.melconway.comЭволюция носителей информации: о перфокартах, магнитных плёнках и дискетах
https://habr.com/ru/companies/ocz/articles/385663/Цикл статей «Разбираемся с coroutine в Kotlin»
https://habr.com/ru/articles/815407/История Корутин. От асинхронного JS и Python до Go и C++20
https://youtu.be/wOeilt_ZQ70?si=e8S9zl9p9y2snML4Роман Елизаров (ссылка на видео)
https://www.youtube.com/watch?v=rB5Q3y73FToЗа кулисами асинхронности: корутины, горутины и правда между ними
https://habr.com/en/companies/oleg-bunin/articles/958566/Как распространялся open-source-софт в 1992 году: Walnut Creek Software
https://habr.com/en/companies/ru_mts/articles/799799/
Dhwtj
Почему корутины могут переключаться быстрее чем системные потоки?
Изоляция у корутин неполная, доверительная, одно адресное пространство, те же страницы и права
У корутин кооперативная многозадачность, может зависнуть, планировщик тривиальный
Остальное это следствие