Сопрограммы на С++, начальный уровень
Автор: /A\
Coroutine (сопрограмма, корутина) — функция, выполнение которой можно явно прерываться методами языка программирования. В отличие от прерывания выполнения потока (thread), который реализован в ОС и происходит неявно и в любой момент. Здесь я разбираю в чем преимущество корутин и какие корутины добавлены в C++20.
Предварительно с терминологией можно ознакомиться в Википедии: сопрограмма
Рассмотрим как работает простой код на уровне железа и ОС:
auto buf = new Buffer{ 10_Mb }; ReadFile("file.name", buf );
В двух строках происходит множество неявных прерываний:
auto buf = new Buffer{ 10_Mb };
Выделяется память для записи файла в RAM. Внутри вызывается аллокатор памяти, который обычно защищен мютексом (wiki). Если в этот же момент в другом потоке происходит еще одно выделение памяти, то мютекс позволит выполниться одному потоку, а другой остановится и будет разбужен ОС спустя какое-то время.
ReadFile("file.name", buf );
Происходит чтение из файла. Доступ к диску медленный, поэтому ОС останавливает поток и возобновляет его, когда чтение из файла завершится.
Получается в каждой строке кода происходит неявная остановка выполнения потока, что приводит к смене контекста (context switch), который занимает более 1мс. Когда поток остановлен, его место занимает другой поток, сколько он выполняется - неизвестно, а значит нет гарантий, когда именно возобновится выполнение нашего потока.
Подход с использованием потоков оказался слишком медленным, так появились различные асинхронные алгоритмы, например с использованием thread pool и promise:
AllocBuffer(10_Mb ) .then( []( Buffer buf) { ReadFile( "file.name", buf ) .then( []( Buffer buf, size_t readn) { // делаем что-то с загруженными данными }); });