Beispiele für C++-Coroutinen

Beispiele Fur C Coroutinen



Coroutinen bieten eine Sprachfunktion, die es Ihnen ermöglicht, den asynchronen Code organisierter und linearer zu schreiben und so einen strukturierten und sequenziellen Ansatz zu fördern. Sie bieten einen Mechanismus zum Anhalten und Neustarten der Ausführung einer Funktion an bestimmten Stellen, ohne den gesamten Thread anzuhalten. Coroutinen sind hilfreich bei der Bearbeitung von Aufgaben, bei denen auf E/A-Vorgänge gewartet werden muss, z. B. beim Lesen aus einer Datei oder beim Senden eines Netzwerkaufrufs.

Coroutinen basieren auf dem Konzept von Generatoren, bei denen eine Funktion Werte liefern und später wieder aufgenommen werden kann, um die Ausführung fortzusetzen. Coroutinen stellen ein leistungsstarkes Tool zur Verwaltung asynchroner Vorgänge dar und können die Gesamtqualität Ihres Codes erheblich verbessern.

Verwendung von Coroutinen

Coroutinen werden in der modernen Programmierung aus mehreren Gründen benötigt, insbesondere in Sprachen wie C++. Hier sind einige Hauptgründe, warum Coroutinen von Vorteil sind:







Coroutinen bieten eine elegante Lösung für die asynchrone Programmierung. Sie ermöglichen die Erstellung eines Codes, der sequentiell und blockierend erscheint und so leichter zu verstehen und zu verstehen ist. Coroutinen können ihre Ausführung an bestimmten Punkten unterbrechen, ohne die Threads zu blockieren, wodurch ein paralleler Betrieb anderer Aufgaben ermöglicht wird. Dadurch können die Systemressourcen effektiver genutzt werden und die Reaktionsfähigkeit bei Anwendungen erhöht werden, die E/A-Vorgänge erfordern oder auf externe Ereignisse warten.



Sie erleichtern möglicherweise das Verständnis und die Wartung des Codes. Durch die Eliminierung der komplexen Rückrufketten oder Zustandsmaschinen ermöglichen Coroutinen, den Code in einem lineareren und sequenzielleren Stil zu schreiben. Dies verbessert die Codeorganisation, reduziert die Verschachtelung und macht die Logik leicht verständlich.



Coroutinen bieten eine strukturierte Möglichkeit, mit Parallelität und Parallelität umzugehen. Sie ermöglichen es Ihnen, die komplexen Koordinationsmuster und asynchronen Arbeitsabläufe mithilfe einer intuitiveren Syntax auszudrücken. Im Gegensatz zu herkömmlichen Threading-Modellen, bei denen die Threads möglicherweise blockiert sind, können Coroutinen Systemressourcen freigeben und ein effizientes Multitasking ermöglichen.





Lassen Sie uns einige Beispiele erstellen, um die Implementierung von Coroutinen in C++ zu demonstrieren.

Beispiel 1: Grundlegende Coroutinen

Das grundlegende Coroutinen-Beispiel wird im Folgenden bereitgestellt:



#include

#include

Struktur DiesCorout {

Struktur Versprechenstyp {

DiesCorout get_return_object ( ) { zurückkehren { } ; }

std :: suspend_never initial_suspend ( ) { zurückkehren { } ; }

std :: suspend_never final_suspend ( ) Nein, außer { zurückkehren { } ; }

Leere unbehandelte Ausnahme ( ) { }

Leere return_void ( ) { }

} ;

bool wait_ready ( ) { zurückkehren FALSCH ; }

Leere wait_suspend ( std :: coroutine_handle <> H ) { }

Leere wait_resume ( ) { std :: cout << „Die Coroutine wird wieder aufgenommen.“ << std :: endl ; }

} ;

ThisCorout foo ( ) {

std :: cout << „Die Coroutine hat begonnen.“ << std :: endl ;

co_await std :: suspend_always { } ;

co_return ;

}

int hauptsächlich ( ) {

Auto cr = foo ( ) ;

std :: cout << „Die Coroutine wird erstellt.“ << std :: endl ;

cr. wait_resume ( ) ;

std :: cout << „Coroutine fertig.“ << std :: endl ;

zurückkehren 0 ;

}

Lassen Sie uns den zuvor bereitgestellten Code durchgehen und ihn im Detail erklären:

Nachdem wir die erforderlichen Header-Dateien eingefügt haben, definieren wir die Struktur „ThisCorout“, die eine Coroutine darstellt. Innerhalb von „ThisCorout“ ist eine weitere Struktur namens „promise_type“ definiert, die das Coroutine-Versprechen verarbeitet. Diese Struktur stellt verschiedene Funktionen bereit, die von der Coroutine-Maschinerie benötigt werden.

Innerhalb der Klammern verwenden wir die Funktion get_return_object(). Es gibt das Coroutine-Objekt selbst zurück. In diesem Fall wird ein leeres „ThisCorout“-Objekt zurückgegeben. Anschließend wird die Funktion initial_suspend() aufgerufen, die das Verhalten beim ersten Start der Coroutine bestimmt. Das std::suspend_never bedeutet, dass die Coroutine zunächst nicht angehalten werden sollte.

Danach haben wir die Funktion final_suspend(), die das Verhalten bestimmt, wenn die Coroutine kurz vor dem Ende steht. Das std::suspend_never bedeutet, dass die Coroutine nicht vor ihrer Finalisierung angehalten werden sollte.

Wenn eine Coroutine eine Ausnahme auslöst, wird die Methode unhandled_Exception() aufgerufen. In diesem Beispiel handelt es sich um eine leere Funktion, aber Sie können die Ausnahmen nach Bedarf behandeln. Wenn die Coroutine beendet wird, ohne einen Wert zu liefern, wird die Methode return_void() aufgerufen. In diesem Fall handelt es sich ebenfalls um eine leere Funktion.

Wir definieren außerdem drei Mitgliedsfunktionen innerhalb von „ThisCorout“. Die Funktion „await_ready()“ wird aufgerufen, um zu prüfen, ob die Coroutine bereit ist, die Ausführung fortzusetzen. In diesem Beispiel wird immer „false“ zurückgegeben, was darauf hinweist, dass die Coroutine nicht bereit ist, sofort fortzufahren. Wenn die Coroutine angehalten werden soll, wird die Methodeawait_suspend() aufgerufen. Hier handelt es sich um eine leere Funktion, was bedeutet, dass keine Suspendierung erforderlich ist. Das Programm ruft „await_resume()“ auf, wenn die Coroutine nach der Unterbrechung wieder aufgenommen wird. Es wird lediglich eine Meldung ausgegeben, die besagt, dass die Coroutine wieder aufgenommen wurde.

Die nächsten Codezeilen definieren die Coroutinenfunktion foo(). In foo() geben wir zunächst eine Nachricht aus, die besagt, dass die Coroutine gestartet wurde. Anschließend wird co_await std::suspend_always{} verwendet, um die Coroutine anzuhalten und anzuzeigen, dass sie zu einem späteren Zeitpunkt wieder aufgenommen werden kann. Die co_return-Anweisung wird verwendet, um die Coroutine zu beenden, ohne einen Wert zurückzugeben.

In der Funktion main() konstruieren wir ein Objekt „cr“ vom Typ „ThisCorout“, indem wir foo() aufrufen. Dadurch wird die Coroutine erstellt und gestartet. Anschließend wird eine Meldung ausgegeben, die besagt, dass die Coroutine erstellt wurde. Als nächstes rufen wir die Funktion „await_resume()“ für das Coroutine-Objekt „cr“ auf, um dessen Ausführung fortzusetzen. Innerhalb von „await_resume()“ wird die Meldung „Die Coroutine wird fortgesetzt“ gedruckt. Abschließend zeigen wir eine Meldung an, die besagt, dass die Coroutine abgeschlossen ist, bevor das Programm beendet wird.

Wenn Sie dieses Programm ausführen, sieht die Ausgabe wie folgt aus:

Beispiel 2: Coroutine mit Parametern und Ertrag

Für diese Veranschaulichung stellen wir nun einen Code bereit, der die Verwendung von Coroutinen mit Parametern und Yield in C++ demonstriert, um ein generatorähnliches Verhalten zur Erzeugung einer Zahlenfolge zu erzeugen.

#include

#include

#include

Struktur NEUCoroutine {

Struktur p_type {

std :: Vektor < int > Werte ;

NEUCoroutine get_return_object ( ) { zurückkehren { } ; }

std :: suspend_always initial_suspend ( ) { zurückkehren { } ; }

std :: suspend_always final_suspend ( ) Nein, außer { zurückkehren { } ; }

Leere unbehandelte Ausnahme ( ) { }

Leere return_void ( ) { }

std :: suspend_always Ertragswert ( int Wert ) {

Werte. push_back ( Wert ) ;

zurückkehren { } ;

}

} ;

std :: Vektor < int > Werte ;

Struktur Iterator {

std :: coroutine_handle <> chorus_handle ;

Bool-Operator != ( const Iterator & andere ) const { zurückkehren chorus_handle != andere. chorus_handle ; }

Iterator & Operator ++ ( ) { chorus_handle. wieder aufnehmen ( ) ; zurückkehren * Das ; }

int Operator * ( ) const { zurückkehren chorus_handle. versprechen ( ) . Werte [ 0 ] ; }

} ;

Iterator beginnt ( ) { zurückkehren Iterator { std :: coroutine_handle < p_type >:: from_promise ( versprechen ( ) ) } ; }

Ende des Iterators ( ) { zurückkehren Iterator { nullptr } ; }

std :: coroutine_handle < p_type > versprechen ( ) { zurückkehren
std :: coroutine_handle < p_type >:: from_promise ( * Das ) ; }

} ;

NEUCoroutine generiert Zahlen ( ) {

co_yield 5 ;

co_yield 6 ;

co_yield 7 ;

}

int hauptsächlich ( ) {

NEUCoroutine nc = generierenNumbers ( ) ;

für ( int Wert : nc ) {

std :: cout << Wert << ' ' ;

}

std :: cout << std :: endl ;

zurückkehren 0 ;

}

Im vorherigen Code stellt die Struktur NEWCoroutine einen Coroutine-basierten Generator dar. Es enthält eine verschachtelte „p_type“-Struktur, die als Versprechenstyp für die Coroutine dient. Die p_type-Struktur definiert die Funktionen, die von der Coroutine-Maschinerie benötigt werden, wie z. B. get_return_object(), initial_suspend(), final_suspend(), unhandled_Exception() und return_void(). Die p_type-Struktur enthält auch die Funktion yield_value(int value), mit der die Werte aus der Coroutine ermittelt werden. Es fügt den bereitgestellten Wert zum Wertevektor hinzu.

Die NEWCoroutine-Struktur enthält die Mitgliedsvariable std::vector namens „values“, die die generierten Werte darstellt. Innerhalb der NEWCoroutine gibt es einen verschachtelten Strukturiterator, der es ermöglicht, über die generierten Werte zu iterieren. Es enthält ein coro_handle, das ein Handle für die Coroutine ist und die Operatoren wie !=, ++ und * für die Iteration definiert.

Wir verwenden die Funktion begin(), um einen Iterator am Anfang der Coroutine zu erstellen, indem wir das coro_handle aus dem p_type-Versprechen erhalten. Während die Funktion end() einen Iterator erstellt, der das Ende der Coroutine darstellt und mit einem nullptr coro_handle erstellt wird. Danach wird die Funktion Promise() verwendet, um den Promise-Typ zurückzugeben, indem ein coroutine_handle aus dem p_type-Promise erstellt wird. Die Funktion „generateNumbers()“ ist eine Coroutine, die mithilfe des Schlüsselworts „co_yield“ drei Werte – 5, 6 und 7 – liefert.

In der Funktion main() wird eine Instanz der NEWCoroutine mit dem Namen „nc“ durch Aufrufen der Coroutine „generateNumbers()“ erstellt. Dadurch wird die Coroutine initialisiert und ihr Zustand erfasst. Eine bereichsbasierte „for“-Schleife wird verwendet, um über die Werte von „nc“ zu iterieren, und jeder Wert wird ausgegeben, der mithilfe von std::cout durch ein Leerzeichen getrennt ist.

Die generierte Ausgabe lautet wie folgt:

Abschluss

Dieser Artikel demonstriert die Verwendung von Coroutinen in C++. Wir haben zwei Beispiele besprochen. Für die erste Veranschaulichung wird die grundlegende Coroutine in einem C++-Programm mithilfe der Coroutine-Funktionen erstellt. Während die zweite Demonstration durchgeführt wurde, wurden die Coroutinen mit Parametern und Nachgeben verwendet, um ein generatorähnliches Verhalten zu erzeugen und eine Folge von Zahlen zu erstellen.