Что сопрограммы в языке C++20?

Что такое coroutines в c++20?

чем он отличается от" Parallelism2 "или/и" Concurrency2 " (посмотрите на изображение ниже)?

изображение ниже от ISOCPP.

https://isocpp.org/files/img/wg21-timeline-2017-03.png

enter image description here

3 ответов


на абстрактном уровне Coroutines отделили идею наличия состояния выполнения от идеи наличия потока выполнения.

SIMD (single instruction multiple data) имеет несколько "потоков выполнения", но только одно состояние выполнения (оно работает только с несколькими данными). Возможно, параллельные алгоритмы немного похожи на это, поскольку у вас есть одна "программа", работающая на разных данных.

Threading имеет несколько "потоков выполнения" и несколько состояний выполнения. У вас есть более одной программы и более одного потока выполнения.

Coroutines имеет несколько состояний выполнения, но не владеет потоком выполнения. У вас есть программа, и программа имеет состояние, но у нее нет потока выполнения.


самый простой пример сопрограммы являются генераторами или enumerables из других языков.

в псевдо код:

function Generator() {
  for (i = 0 to 100)
    produce i
}

на Generator называется, и в первый раз это называется возвращает 0. Его состояние запоминается (насколько состояние зависит от реализации корутинов), и в следующий раз, когда вы его назовете, он продолжает там, где он остановился. Так он возвращает 1 в следующий раз. Затем 2.

наконец, он достигает конца цикла и падает с конца функции; корутина завершена. (То, что происходит здесь, зависит от языка, о котором мы говорим; в python он создает исключение).

Coroutines довести эту возможность до С.++

есть два вида сопрограммы; stackful и stackless.

бессекционная корутина хранит только локальные переменные в своем состоянии и месте выполнения.

stackful coroutine хранит весь стек (как поток).

Stackless coroutines могут быть весьма облегченны. Последнее предложение, которое я прочитал, включало в основном переписывание вашей функции во что-то вроде лямбды; все локальные переменные переходят в состояние объект и метки используются для перехода в / из места, где корутина "производит" промежуточные результаты.

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

Boost имеет реализацию stackful coroutines; он позволяет вызывать функцию для выхода для вас. Stackful coroutines более мощные, но и более дорогой.


больше к сопрограммам, чем простой генератор. Вы можете подождать в сопрограмма сопрограмма, которая позволяет создавать сопрограммы в полезной манере.

Coroutines, например if, loops и function calls, - это еще один вид "структурированного Гото", который позволяет более естественным образом выражать определенные полезные шаблоны (например, государственные машины).


конкретной реализации сопрограмм в C++ - это немного интересный.

на самом базовом уровне он добавляет несколько ключевых слов на C++:co_return co_await co_yield, вместе с некоторыми типами библиотек, которые работают с ними.

функция становится сопрограмма, имея одним из тех, кто в своем теле. Таким образом, из их декларации они неотличимы от функций.

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

простейшая корутина-это генератор:

generator<int> get_integers( int start=0, int step=1 ) {
  for (int current=start; current+= step)
    co_yield current;
}

co_yield приостанавливает выполнение функций, сохраняет это состояние в generator<int>, а затем возвращает значение current до generator<int>.

вы можете перебрать все целые вернулись.

co_await между тем позволяет вам сращивать одну корутину на другой. Если вы находитесь в одной сопрограммы и вам нужны результаты ожидать вещь (часто сопрограмма) перед переходом, вы co_await на нем. Если они будут готовы, ты сразу переходим; если нет, то приостановить, пока ожидать вас ждем готово.

std::future<std::expected<std::string>> load_data( std::string resource )
{
  auto handle = co_await open_resouce(resource);
  while( auto line = co_await read_line(handle)) {
    if (std::optional<std::string> r = parse_data_from_line( line ))
       co_return *r;
  }
  co_return std::unexpected( resource_lacks_data(resource) );
}

load_data - это сопрограмма, которая генерирует std::future когда именованный ресурс открыт, и нам удается проанализировать до точки, где мы нашли запрошенные данные.

open_resource и read_lineС вероятно, асинхронные сопрограммы, которые открывают файл и читают строки из него. The co_await соединяет состояние приостановки и готовности load_data для их развития.

C++ coroutines намного более гибкие, чем это, поскольку они реализованы как минимальный набор языковых функций поверх типов пользовательского пространства. Типы пользовательского пространства эффективно определяют, что co_return co_await и co_yield mean -- я видел, как люди используют его для реализации монадических необязательных выражений, таких что co_await на пустой опционально автоматически propogates порожнего состояния на внешний дополнительно:

modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
  return (co_await a) + (co_await b);
}

вместо

std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
  if (!a) return std::nullopt;
  if (!b) return std::nullopt;
  return *a + *b;
}

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

предыдущая экспериментальная реализация coroutine от Microsoft использовала скопированные стеки, чтобы вы могли даже вернуться из deep nested функции. Но эта версия была отклонена комитетом C++. Вы можете получить эту реализацию, например, с библиотекой волокон Boosts.


coroutines должны быть (в C++) функциями, которые могут "ждать" завершения какой-либо другой процедуры и предоставлять все необходимое для приостановки, приостановки, ожидания, выполнения процедуры. функцию, наиболее интересные для C++ ребята, что сопрограммы в идеале бы нет места в стеке...C# уже может сделать что-то подобное с await и yield, но C++, возможно, придется перестроить, чтобы получить его.

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

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