zurück zum Artikel

C++20: Mehr Details zu Coroutinen

Rainer Grimm

Weiter mit dem Überblick zu Coroutinen: In C++20 wird es jedoch keine Coroutinen geben, sondern ein Framework, um Coroutinen zu implementieren.

Nachdem der letzte Artikel "C++20: Coroutinen – ein erster Überblick [1]" in Coroutinen einführte, geht es heute um weitere Details. Gerne möchte ich wiederholen: Wir erhalten in C++20 keine Coroutinen, sondern ein Framework, um Coroutinen zu implementieren.

Mein Ziel in diesem und weiteren Artikeln ist es, dieses Framework zum Implementieren eigener Coroutinen zu erklären. Am Ende kannst du Coroutinen erzeugen oder existierende Implementierungen von Couroutinen wie die exzellente cppcoro- [2]Umsetzung von Lewis Baker verwenden.

Der heutige Artikel ist ein Weder-noch-Artikel. Weder stellt dieser Artikel einen Überblick dar, noch steigt er tief in das Coroutinen-Framework ein.

Die erste Frage, die du zu Coroutinen hast, wird wohl sein: Wann sollten Coroutinen verwendet werden?

C++20: Mehr Details zu Coroutinen

Coroutinen werden gerne für Event-getriebene Applikationen [3] verwendet. Diese können eine Simulation, ein Spiel, ein Server, ein Benutzerinterface oder auch ein Algorithmus sein. So schrieb ich zum Beispiel vor ein paar Jahren einen Simulator für einen Defibrillator. Der Defibrillator kam vor allem für die klinischen Usability-Tests zum Einsatz und ist eine Event-getriebenen Applikation. Daher setze ich ihn mithilfe des Event-getriebenen Frameworks twisted [4] in Python um.

Coroutinen werden auch gerne für kooperatives Multitasking eingesetzt. Die zentrale Idee des kooperativen Multitaskings ist es, dass sich jeder Task so viel Zeit nimmt, wie er benötigt. Kooperatives Multitasking unterscheidet sich vom präemptiven Multitasking darin, dass ein Scheduler entscheidet, wie lange jeder Task die CPU erhält. Kooperatives Multitasking erlaubt es, Concurrency einfacher umzusetzen, da ein Task nicht in einem kritischen Bereich unterbrochen wird. Wenn du mehr Aufklärung zu den Begriffen kooperativ und präemptiv suchst, kann ich nur diesen exzellenten Überblickartikel empfehlen: "Cooperative vs. Preemptive: a quest to maximize concurrency power [5]".

Coroutinen in C++20 sind asymmetrisch, first-class und stackless:

Gor Nishanov, der maßgeblich an der Standardisierung von Coroutinen in C++ beteiligt ist, stellt die Designziele von Coroutinen vor. Sie sollen

Eine Funktion, die die Schlüsselworte co_return, co_yield oder co_return verwendet, wird automatisch zur Coroutine:

Der C++20-Standard definiert bereits zwei Awaitables als elementare Bausteine: std::suspend_always und std::suspend_never.

struct suspend_always {
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};

std::suspend_always pausiert immer, da await_ready false zurückgibt. Genau das Gegenteil gilt für das zweite Awaitable std::suspend_never.

struct suspend_never {
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};

Ich hoffe, dass das folgende Beispiel es einfacher macht, diese Theorie zu verdauen. Ein Server ist das "hello world"-Beispiel für eine Coroutine.

Ein Server ist eine Event-getriebene Applikation. Er wartet typischerweise in einer Evenschleife auf Clientanfragen.Der folgende Codeschnipsel stellt die Struktur eines einfachen Servers vor:

Acceptor acceptor{443};               // (1)

while (true){
Socket socket= acceptor.accept(); // blocking (2)
auto request= socket.read(); // blocking (3)
auto response= handleRequest(request);
socket.write(response); // blocking (4)
}

Der sequenzielle Server beantwortet jede Clientanfrage in demselben Thread. Der Server lauscht auf den Post 443 (Zeile 1), nimmt jede Verbindung an (Zeile 2), liest die ankommenden Daten von dem Client (Zeile 3) ein und schickt seine Antwort an den Client zurück (Zeile 4). Die Aufrufe in den Zeilen 2 bis 4 sind blockierend.

Dank co_await lassen sich die blockierenden Aufrufe einfach pausieren und wieder aufnehmen. Der ressourcenintensive blockierende Server wird dadurch zum ressourcenschonenden wartenden Server:

Acceptor acceptor{443};

while (true){
Socket socket= co_await acceptor.accept();
auto request= co_await socket.read();
auto response= handleRequest(request);
co_await socket.write(response);
}

Du vermutest es wohl schon. Der entscheidende Ausdruck, um Coroutinen zu verstehen, sind die awaitable-Ausdrücke expr in co_await expr. Sie müssen die Funktionen await_ready, await_suspend und await_resume umsetzen.

Das Framework für das Schreiben von Coroutinen besteht aus mehr als 20 Funktionen. Diese gilt es zumindest teilweise zu implementieren oder zur überladen. Mit meinem nächsten Artikel tauche ich tiefer in das Framework ein.

Aufgrund des Coronavirus biete ich alle meine Schulungen jetzt auch online an. Ich halte bereits seit mehr als 10 Jahren Online-Seminare. Dank der modernen Webkonferenz-Werkzeuge ist ein Online-Seminar mehr als ein Ersatz für eine Präsenzschulung. Ein Online-Seminar bietet Mehrwert gegenüber einer Präsenzschulung an.

Online-Seminare:

Wir sollten die aktuelle Krise als Chance sehen und nutzen, analoge Muster durch digitale Lösungen zu ersetzen und das Mehr an Zeit sinnvoll zu investieren. Eine Krise lässt sich nicht durch Aussitzen lösen. Ich habe die Preise für meine Online-Seminare während der Krise deutlich reduziert. Wem der Preis noch zu hoch ist, der kann direkt mit mir (schulung@ModernesCpp.de [9]) Kontakt aufnehmen. ( [10])


URL dieses Artikels:
https://www.heise.de/-4692662

Links in diesem Artikel:
[1] https://heise.de/-4687457
[2] https://github.com/lewissbaker/cppcoro
[3] https://github.com/lewissbaker/cppcoro
[4] https://twistedmatrix.com/trac/
[5] https://medium.com/traveloka-engineering/cooperative-vs-preemptive-a-quest-to-maximize-concurrency-power-3b10c5a920fe
[6] https://heise.de/-4687457
[7] https://www.modernescpp.de/index.php/c/2-c/25-c-11-und-c-14-online
[8] https://www.modernescpp.de/index.php/c/2-c/26-embedded-programmierung-mit-modernem-c-online
[9] mailto:schulung@ModernesCpp.de
[10] mailto:rainer@grimm-jaud.de