C++ Coroutines-voorbeelden

C Coroutines Voorbeelden



Coroutines bieden een taalfunctie waarmee u de asynchrone code op een meer georganiseerde en lineaire manier kunt schrijven, waardoor een gestructureerde en sequentiële aanpak wordt bevorderd. Ze bieden een mechanisme om de uitvoering van een functie op bepaalde momenten te onderbreken en opnieuw te starten zonder de hele thread te stoppen. Coroutines zijn handig bij het afhandelen van taken waarbij moet worden gewacht op I/O-bewerkingen, zoals het lezen uit een bestand of het verzenden van een netwerkoproep.

Coroutines zijn gebaseerd op het concept van generatoren waarbij een functie waarden kan opleveren en later kan worden hervat om de uitvoering voort te zetten. Coroutines bieden een krachtig hulpmiddel om de asynchrone bewerkingen te beheren en kunnen de algehele kwaliteit van uw code aanzienlijk verbeteren.

Gebruik van Coroutines

Coroutines zijn om verschillende redenen nodig in modern programmeren, vooral in talen als C++. Hier zijn enkele belangrijke redenen waarom coroutines gunstig zijn:







Coroutines bieden een elegante oplossing voor asynchrone programmering. Ze maken het mogelijk om een ​​code te creëren die sequentieel en blokkerend lijkt en die eenvoudiger te begrijpen en te begrijpen is. Coroutines kunnen hun uitvoering op specifieke punten opschorten zonder de threads te blokkeren, waardoor een parallelle werking van andere taken mogelijk wordt. Hierdoor kunnen de systeembronnen effectiever worden gebruikt en wordt de responsiviteit vergroot in toepassingen waarbij I/O-bewerkingen betrokken zijn of die op externe gebeurtenissen wachten.



Ze kunnen de code gemakkelijker te begrijpen en te onderhouden maken. Door de complexe callback-ketens of statusmachines te elimineren, zorgen coroutines ervoor dat de code in een meer lineaire en sequentiële stijl kan worden geschreven. Dit verbetert de organisatie van de code, vermindert nesten en maakt de logica gemakkelijk te begrijpen.



Coroutines bieden een gestructureerde manier om met gelijktijdigheid en parallellisme om te gaan. Ze stellen u in staat de complexe coördinatiepatronen en asynchrone workflows uit te drukken met behulp van een meer intuïtieve syntaxis. In tegenstelling tot traditionele threadingmodellen waarbij de threads mogelijk worden geblokkeerd, kunnen coroutines systeembronnen vrijmaken en efficiënt multitasken mogelijk maken.





Laten we enkele voorbeelden maken om de implementatie van coroutines in C++ te demonstreren.

Voorbeeld 1: Basiscoroutines

Het basiscoroutinesvoorbeeld wordt als volgt gegeven:



#include

#include

structureren DezeCorout {

structureren belofte_type {

DitCorout get_return_object ( ) { opbrengst { } ; }

soa :: opschorten_nooit initial_suspend ( ) { opbrengst { } ; }

soa :: opschorten_nooit definitieve_opschorting ( ) neebehalve { opbrengst { } ; }

leegte onbehandelde uitzondering ( ) { }

leegte return_void ( ) { }

} ;

bool wachten_klaar ( ) { opbrengst vals ; }

leegte wachten_opschorten ( soa :: coroutine_handle <> H ) { }

leegte wachten_hervatten ( ) { soa :: uit << 'De Coroutine wordt hervat.' << soa :: eindl ; }

} ;

Deze Corout foo ( ) {

soa :: uit << 'De Coroutine is begonnen.' << soa :: eindl ;

co_await std :: opschorten_altijd { } ;

co_retour ;

}

int voornaamst ( ) {

auto kr = foe ( ) ;

soa :: uit << 'De Coroutine is gemaakt.' << soa :: eindl ;

kr. wachten_hervatten ( ) ;

soa :: uit << 'Coroutine is klaar.' << soa :: eindl ;

opbrengst 0 ;

}

Laten we de eerder verstrekte code doornemen en in detail uitleggen:

Nadat we de vereiste headerbestanden hebben opgenomen, definiëren we de “ThisCorout” -structuur die een coroutine vertegenwoordigt. Binnen de “ThisCorout” is een andere structuur gedefinieerd die “promise_type” is en die de coroutine-belofte afhandelt. Deze structuur biedt verschillende functies die vereist zijn voor de coroutine-machinerie.

Binnen de haakjes gebruiken we de functie get_return_object(). Het retourneert het coroutine-object zelf. In dit geval retourneert het een leeg object “ThisCorout”. Vervolgens wordt de functie initial_suspend() aangeroepen, die het gedrag bepaalt wanneer de coroutine voor het eerst wordt gestart. De std::suspend_never betekent dat de coroutine aanvankelijk niet mag worden opgeschort.

Daarna hebben we de functie final_suspend() die het gedrag bepaalt wanneer de coroutine op het punt staat te eindigen. De std::suspend_never betekent dat de coroutine niet mag worden opgeschort voordat deze is voltooid.

Als een coroutine een uitzondering genereert, wordt de methode unhandled_exception() aangeroepen. In dit voorbeeld is het een lege functie, maar u kunt de uitzonderingen indien nodig afhandelen. Wanneer de coroutine eindigt zonder een waarde op te leveren, wordt de return_void() -methode aangeroepen. In dit geval is het ook een lege functie.

Binnen “ThisCorout” definiëren we ook drie lidfuncties. De functie await_ready() wordt aangeroepen om te controleren of de coroutine klaar is om de uitvoering te hervatten. In dit voorbeeld retourneert het altijd false, wat aangeeft dat de coroutine niet klaar is om onmiddellijk te hervatten. Wanneer de coroutine wordt opgeschort, wordt de methode await_suspend() aangeroepen. Hier is het een lege functie, wat betekent dat er geen opschorting nodig is. Het programma roept de await_resume() aan wanneer de coroutine na opschorting wordt hervat. Er wordt alleen een bericht weergegeven waarin staat dat de coroutine is hervat.

De volgende regels van de code definiëren de foo() coroutine-functie. Binnen foo() beginnen we met het afdrukken van een bericht waarin staat dat de coroutine is gestart. Vervolgens wordt co_await std::suspend_always{} gebruikt om de coroutine op te schorten en geeft aan dat deze op een later tijdstip kan worden hervat. De co_return-instructie wordt gebruikt om de coroutine af te ronden zonder enige waarde terug te geven.

In de functie main() construeren we een object “cr” van het type “ThisCorout” door foo() aan te roepen. Hierdoor wordt de coroutine gemaakt en gestart. Vervolgens wordt een bericht afgedrukt waarin staat dat de coroutine is gemaakt. Vervolgens roepen we await_resume() op het “cr” coroutine-object aan om de uitvoering ervan te hervatten. Binnen await_resume() wordt het bericht 'De Coroutine is hervat' afgedrukt. Ten slotte geven we een bericht weer waarin staat dat de coroutine is voltooid voordat het programma wordt beëindigd.

Wanneer u dit programma uitvoert, is de uitvoer als volgt:

Voorbeeld 2: Coroutine met parameters en opbrengst

Voor deze illustratie bieden we nu een code die het gebruik van coroutines met parameters en yielding in C++ demonstreert om een ​​generatorachtig gedrag te creëren om een ​​reeks getallen te produceren.

#include

#include

#include

structureren NIEUWCoroutine {

structureren p_type {

soa :: vector < int > waarden ;

NIEUWCoroutine get_return_object ( ) { opbrengst { } ; }

soa :: opschorten_altijd initial_suspend ( ) { opbrengst { } ; }

soa :: opschorten_altijd definitieve_opschorting ( ) neebehalve { opbrengst { } ; }

leegte onbehandelde uitzondering ( ) { }

leegte return_void ( ) { }

soa :: opschorten_altijd opbrengst_waarde ( int waarde ) {

waarden. terugduwen ( waarde ) ;

opbrengst { } ;

}

} ;

soa :: vector < int > waarden ;

structureren iterator {

soa :: coroutine_handle <> refrein_handle ;

bool-operator != ( const iterator & ander ) const { opbrengst refrein_handle != ander. refrein_handle ; }

iterator & exploitant ++ ( ) { refrein_handle. cv ( ) ; opbrengst * dit ; }

int exploitant * ( ) const { opbrengst refrein_handle. belofte ( ) . waarden [ 0 ] ; }

} ;

iterator begint ( ) { opbrengst iterator { soa :: coroutine_handle < p_type >:: van_belofte ( belofte ( ) ) } ; }

iterator einde ( ) { opbrengst iterator { nulptr } ; }

soa :: coroutine_handle < p_type > belofte ( ) { opbrengst
soa :: coroutine_handle < p_type >:: van_belofte ( * dit ) ; }

} ;

NIEUWCoroutine genererenNumbers ( ) {

co_opbrengst 5 ;

co_opbrengst 6 ;

co_opbrengst 7 ;

}

int voornaamst ( ) {

NIEUWCoroutine nc = getallen genereren ( ) ;

voor ( int waarde : nc ) {

soa :: uit << waarde << ' ' ;

}

soa :: uit << soa :: eindl ;

opbrengst 0 ;

}

In de vorige code vertegenwoordigt de NEWCoroutine-structuur een op coroutine gebaseerde generator. Het bevat een geneste “p_type”-structuur die dient als het beloftetype voor de coroutine. De p_type-structuur definieert de functies die vereist zijn door de coroutine-machinerie, zoals get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() en return_void(). De p_type-structuur bevat ook de yield_value(int value)-functie die wordt gebruikt om de waarden uit de coroutine op te leveren. Het voegt de opgegeven waarde toe aan de waardenvector.

De NEWCoroutine-structuur bevat de std::vector lidvariabele genaamd “values” die de gegenereerde waarden vertegenwoordigt. Binnen NEWCoroutine bevindt zich een geneste struct-iterator waarmee de gegenereerde waarden kunnen worden herhaald. Het bevat een coro_handle die een handvat is voor de coroutine en de operatoren definieert zoals !=, ++ en * voor iteratie.

We gebruiken de functie begin() om een ​​iterator te maken aan het begin van de coroutine door de coro_handle te verkrijgen uit de p_type-belofte. Terwijl de functie end() een iterator creëert die het einde van de coroutine vertegenwoordigt en is geconstrueerd met een nullptr coro_handle. Daarna wordt de functie Promise() gebruikt om het beloftetype te retourneren door een coroutine_handle te maken op basis van de p_type-belofte. De functie genererenNumbers() is een coroutine die drie waarden oplevert – 5, 6 en 7 – met behulp van het trefwoord co_yield.

In de functie main() wordt een exemplaar van NEWCoroutine met de naam “nc” gemaakt door de genererenNumbers() coroutine aan te roepen. Hierdoor wordt de coroutine geïnitialiseerd en de staat ervan vastgelegd. Een op bereik gebaseerde “for”-lus wordt gebruikt om de waarden van “nc” te herhalen, en elke waarde wordt afgedrukt, gescheiden door een spatie met behulp van de std::cout.

De gegenereerde uitvoer is als volgt:

Conclusie

Dit artikel demonstreert het gebruik van coroutines in C++. We bespraken twee voorbeelden. Voor de eerste illustratie wordt de basiscoroutine gemaakt in een C++-programma met behulp van de coroutine-functies. Terwijl de tweede demonstratie werd uitgevoerd door de coroutines met parameters te gebruiken en toe te geven om een ​​generatorachtig gedrag te genereren om een ​​reeks getallen te creëren.