Eksempler på C++ Coroutines

Eksempler Pa C Coroutines



Coroutines gir en språkfunksjon som lar deg skrive den asynkrone koden på en mer organisert og lineær måte, og fremmer en strukturert og sekvensiell tilnærming. De gir en mekanisme for å sette på pause og starte kjøringen av en funksjon på nytt i bestemte tilfeller uten å stoppe hele tråden. Coroutines er nyttige når du håndterer oppgaver som krever venting på I/O-operasjoner, som å lese fra en fil eller sende et nettverksanrop.

Coroutines er basert på konseptet med generatorer der en funksjon kan gi verdier og senere gjenopptas for å fortsette utførelsen. Coroutines gir et kraftig verktøy for å administrere de asynkrone operasjonene og kan i stor grad forbedre den generelle kvaliteten på koden din.

Bruk av Coroutines

Coroutines er nødvendig av flere grunner i moderne programmering, spesielt i språk som C++. Her er noen viktige grunner til at korutiner er fordelaktige:







Coroutines gir en elegant løsning for asynkron programmering. De gjør det mulig å lage en kode som virker sekvensiell og blokkerende som er enklere å resonnere rundt og forstå. Coroutines kan suspendere utførelsen på bestemte punkter uten å blokkere trådene, noe som muliggjør en parallell operasjon av andre oppgaver. På grunn av dette kan systemressursene brukes mer effektivt, og responsen økes i applikasjoner som involverer I/O-operasjoner eller venter på eksterne hendelser.



De kan gjøre koden lettere å forstå og vedlikeholde. Ved å eliminere de komplekse tilbakeringingskjedene eller tilstandsmaskiner, gjør coroutines det mulig å skrive koden i en mer lineær og sekvensiell stil. Dette forbedrer kodeorganiseringen, reduserer nesting og gjør logikken lett å forstå.



Coroutines gir en strukturert måte å håndtere samtidighet og parallellitet. De lar deg uttrykke de komplekse koordinasjonsmønstrene og asynkrone arbeidsflytene ved å bruke en mer intuitiv syntaks. I motsetning til tradisjonelle threading-modeller der trådene kan være blokkert, kan coroutines frigjøre systemressursene og muliggjøre en effektiv multitasking.





La oss lage noen eksempler for å demonstrere implementeringen av korutiner i C++.

Eksempel 1: Grunnleggende Coroutines

Det grunnleggende koroutineksemplet er gitt i følgende:



#include

#inkluder

struktur Denne Corout {

struktur løfte_type {

ThisCorout get_return_object ( ) { komme tilbake { } ; }

std :: suspend_aldri initial_suspend ( ) { komme tilbake { } ; }

std :: suspend_aldri final_suspend ( ) ikke unntatt { komme tilbake { } ; }

tomrom ubehandlet unntak ( ) { }

tomrom return_void ( ) { }

} ;

bool await_ready ( ) { komme tilbake falsk ; }

tomrom await_suspend ( std :: coroutine_handle <> h ) { }

tomrom await_resume ( ) { std :: cout << 'Koroutinen er gjenopptatt.' << std :: endl ; }

} ;

Dette Corout foo ( ) {

std :: cout << 'Koroutinen har startet.' << std :: endl ;

co_wait std :: suspend_alltid { } ;

co_return ;

}

int hoved- ( ) {

auto cr = foo ( ) ;

std :: cout << 'The Coroutine er skapt.' << std :: endl ;

cr. await_resume ( ) ;

std :: cout << 'Coroutine ferdig.' << std :: endl ;

komme tilbake 0 ;

}

La oss gå gjennom den tidligere angitte koden og forklare den i detalj:

Etter å ha inkludert de nødvendige overskriftsfilene, definerer vi 'ThisCorout'-strukturen som representerer en coroutine. Inne i 'ThisCorout' er en annen struktur som er 'promise_type' definert som håndterer coroutine-løftet. Denne strukturen gir forskjellige funksjoner som kreves av coroutine-maskineriet.

Innenfor parentesene bruker vi get_return_object()-funksjonen. Den returnerer selve coroutine-objektet. I dette tilfellet returnerer den et tomt 'ThisCorout'-objekt. Deretter aktiveres initial_suspend()-funksjonen som bestemmer oppførselen når koroutinen først startes. Std::suspend_never betyr at koroutinen ikke skal suspenderes først.

Etter det har vi funksjonen final_suspend() som bestemmer oppførselen når koroutinen er i ferd med å fullføres. Std::suspend_never betyr at koroutinen ikke skal suspenderes før den er ferdigstilt.

Hvis en coroutine kaster et unntak, påkalles unhandled_exception()-metoden. I dette eksemplet er det en tom funksjon, men du kan håndtere unntakene etter behov. Når korutinen avsluttes uten å gi en verdi, påkalles return_void()-metoden. I dette tilfellet er det også en tom funksjon.

Vi definerer også tre medlemsfunksjoner i 'ThisCorout'. Funksjonen await_ready() kalles for å sjekke om koroutinen er klar til å gjenoppta kjøringen. I dette eksemplet returnerer den alltid falsk, noe som indikerer at koroutinen ikke er klar til å gjenopptas umiddelbart. Når koroutinen skal suspenderes, kalles metoden await_suspend(). Her er det en tom funksjon som betyr at ingen suspensjon er nødvendig. Programmet kaller await_resume() når koroutinen gjenopptas etter suspensjon. Den sender bare ut en melding som sier at koroutinen er gjenopptatt.

De neste linjene i koden definerer foo() coroutine-funksjonen. Inne i foo() begynner vi med å skrive ut en melding som sier at koroutinen har startet. Deretter brukes co_await std::suspend_always{} til å suspendere koroutinen og indikerer at den kan gjenopptas på et senere tidspunkt. Co_return-setningen brukes til å fullføre koroutinen uten å returnere noen verdi.

I main()-funksjonen konstruerer vi et objekt 'cr' av typen 'ThisCorout' ved å kalle foo(). Dette skaper og starter koroutinen. Deretter skrives det ut en melding som sier at koroutinen er opprettet. Deretter kaller vi await_resume() på 'cr' coroutine-objektet for å gjenoppta utførelsen. Inne i await_resume() skrives meldingen 'The Coroutine is resumed' ut. Til slutt viser vi en melding som sier at koroutinen er fullført før programmet avsluttes.

Når du kjører dette programmet, er utdataene som følger:

Eksempel 2: Coroutine med parametere og avkastning

Nå, for denne illustrasjonen, gir vi en kode som demonstrerer bruken av korutiner med parametere og gir i C++ for å lage en generatorlignende oppførsel for å produsere en tallsekvens.

#include

#include

#inkluder

struktur NYKorutine {

struktur p_type {

std :: vektor < int > verdier ;

NY Coroutine get_return_object ( ) { komme tilbake { } ; }

std :: suspend_alltid initial_suspend ( ) { komme tilbake { } ; }

std :: suspend_alltid final_suspend ( ) ikke unntatt { komme tilbake { } ; }

tomrom ubehandlet unntak ( ) { }

tomrom return_void ( ) { }

std :: suspend_alltid avkastningsverdi ( int verdi ) {

verdier. push_back ( verdi ) ;

komme tilbake { } ;

}

} ;

std :: vektor < int > verdier ;

struktur iterator {

std :: coroutine_handle <> chorus_handle ;

bool operatør != ( konst iterator & annen ) konst { komme tilbake chorus_handle != annen. chorus_handle ; }

iterator & operatør ++ ( ) { chorus_handle. gjenoppta ( ) ; komme tilbake * dette ; }

int operatør * ( ) konst { komme tilbake chorus_handle. love ( ) . verdier [ 0 ] ; }

} ;

iterator begynne ( ) { komme tilbake iterator { std :: coroutine_handle < p_type >:: fra_løfte ( love ( ) ) } ; }

iteratorslutt ( ) { komme tilbake iterator { nullptr } ; }

std :: coroutine_handle < p_type > love ( ) { komme tilbake
std :: coroutine_handle < p_type >:: fra_løfte ( * dette ) ; }

} ;

NY Coroutine genererer tall ( ) {

co_yield 5 ;

co_yield 6 ;

co_yield 7 ;

}

int hoved- ( ) {

NY Coroutine nc = generere tall ( ) ;

til ( int verdi : nc ) {

std :: cout << verdi << ' ' ;

}

std :: cout << std :: endl ;

komme tilbake 0 ;

}

I den forrige koden representerer NEWCoroutine-strukturen en coroutine-basert generator. Den inneholder en nestet 'p_type'-struktur som fungerer som løftetypen for koroutinen. p_type-strukturen definerer funksjonene som kreves av coroutine-maskineriet som get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() og return_void(). p_type-strukturen inkluderer også funksjonen yield_value(int value) som brukes til å gi verdiene fra koroutinen. Den legger til den angitte verdien til verdivektoren.

NEWCoroutine-strukturen inkluderer std::vector -medlemsvariabelen kalt 'verdier' som representerer de genererte verdiene. Inne i NEWCoroutine er det en nestet struktur-iterator som gjør det mulig å iterere over de genererte verdiene. Den har en coro_handle som er et håndtak til coroutinen og definerer operatorene som !=, ++ og * for iterasjon.

Vi bruker start()-funksjonen til å lage en iterator ved starten av koroutinen ved å hente coro_handle fra p_type-løftet. Mens end()-funksjonen lager en iterator som representerer slutten av koroutinen og er konstruert med en nullptr coro_handle. Deretter brukes løfte()-funksjonen for å returnere løftetypen ved å lage en coroutine_handle fra p_typeløftet. GenererNumbers()-funksjonen er en koroutin som gir tre verdier – 5, 6 og 7 – ved å bruke nøkkelordet co_yield.

I main()-funksjonen opprettes en forekomst av NEWCoroutine kalt 'nc' ved å påkalle generNumbers()-koroutinen. Dette initialiserer koroutinen og fanger dens tilstand. En rekkeviddebasert 'for'-løkke brukes til å iterere over verdiene til 'nc', og hver verdi skrives ut som er atskilt med et mellomrom ved å bruke std::cout.

Den genererte utgangen er som følger:

Konklusjon

Denne artikkelen demonstrerer bruken av koroutiner i C++. Vi diskuterte to eksempler. For den første illustrasjonen lages den grunnleggende korutinen i et C++-program ved å bruke korutinfunksjonene. Mens den andre demonstrasjonen ble utført ved å bruke koroutinene med parametere og gi etter for å generere en generatorlignende oppførsel for å lage en tallsekvens.