Eksempler på C++ Coroutines

Eksempler Pa C Coroutines



Coroutines giver en sprogfunktion, der sætter dig i stand til at skrive den asynkrone kode på en mere organiseret og lineær måde, hvilket fremmer en struktureret og sekventiel tilgang. De giver en mekanisme til at pause og genstarte en funktions udførelse i bestemte tilfælde uden at stoppe hele tråden. Coroutiner er nyttige, når du håndterer opgaver, der kræver at vente på I/O-operationer, såsom at læse fra en fil eller sende et netværksopkald.

Coroutines er baseret på konceptet med generatorer, hvor en funktion kan give værdier og senere genoptages for at fortsætte udførelsen. Coroutines giver et kraftfuldt værktøj til at styre de asynkrone operationer og kan i høj grad forbedre den overordnede kvalitet af din kode.

Brug af Coroutines

Coroutiner er nødvendige af flere årsager i moderne programmering, især i sprog som C++. Her er nogle vigtige grunde til, at coroutiner er gavnlige:







Coroutines giver en elegant løsning til asynkron programmering. De gør det muligt at skabe en kode, der fremstår sekventiel og blokerende, som er lettere at ræsonnere om og forstå. Coroutiner kan suspendere deres udførelse på bestemte punkter uden at blokere trådene, hvilket muliggør en parallel drift af andre opgaver. På grund af dette kan systemressourcerne bruges mere effektivt, og reaktionsevnen øges i applikationer, der involverer I/O-operationer eller venter på eksterne hændelser.



De kan gøre koden lettere at forstå og vedligeholde. Ved at eliminere de komplekse tilbagekaldskæder eller tilstandsmaskiner gør coroutiner det muligt at skrive koden i en mere lineær og sekventiel stil. Dette forbedrer kodeorganisationen, reducerer indlejring og gør logikken nem at forstå.



Coroutines giver en struktureret måde at håndtere samtidighed og parallelitet. De giver dig mulighed for at udtrykke de komplekse koordinationsmønstre og asynkrone arbejdsgange ved hjælp af en mere intuitiv syntaks. I modsætning til traditionelle threading-modeller, hvor trådene kan være blokeret, kan coroutines frigøre systemressourcerne og muliggøre en effektiv multitasking.





Lad os lave nogle eksempler for at demonstrere implementeringen af ​​coroutines i C++.

Eksempel 1: Grundlæggende Coroutines

Eksemplet med grundlæggende koroutiner findes i det følgende:



#include

#inkluder

struktur Denne Corout {

struktur løfte_type {

ThisCorout get_return_object ( ) { Vend tilbage { } ; }

std :: suspend_aldrig initial_suspend ( ) { Vend tilbage { } ; }

std :: suspend_aldrig final_suspend ( ) nej undtagen { Vend tilbage { } ; }

ugyldig unhandled_exception ( ) { }

ugyldig return_void ( ) { }

} ;

bool await_ready ( ) { Vend tilbage falsk ; }

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

ugyldig await_resume ( ) { std :: cout << 'Koroutinen er genoptaget.' << std :: endl ; }

} ;

Denne Corout foo ( ) {

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

co_await std :: suspend_always { } ;

co_return ;

}

int vigtigste ( ) {

auto cr = foo ( ) ;

std :: cout << 'Koroutinen er skabt.' << std :: endl ;

cr. await_resume ( ) ;

std :: cout << 'Coroutine færdig.' << std :: endl ;

Vend tilbage 0 ;

}

Lad os gennemgå den tidligere leverede kode og forklare den i detaljer:

Efter at have inkluderet de nødvendige header-filer, definerer vi 'ThisCorout'-strukturen, der repræsenterer en coroutine. Inde i 'ThisCorout' er en anden struktur, som er 'promise_type', defineret, der håndterer coroutine-løftet. Denne struktur giver forskellige funktioner, der kræves af coroutine-maskineriet.

Inden for parenteserne bruger vi funktionen get_return_object() . Det returnerer selve coroutine-objektet. I dette tilfælde returnerer det et tomt 'ThisCorout'-objekt. Derefter aktiveres initial_suspend()-funktionen, som bestemmer adfærden, når coroutinen først startes. Std::suspend_never betyder, at coroutinen ikke bør suspenderes i starten.

Derefter har vi funktionen final_suspend() som bestemmer adfærden, når coroutinen er ved at afslutte. Std::suspend_never betyder, at coroutinen ikke bør suspenderes før dens færdiggørelse.

Hvis en coroutine kaster en undtagelse, kaldes unhandled_exception() metoden. I dette eksempel er det en tom funktion, men du kan håndtere undtagelserne efter behov. Når coroutinen afsluttes uden at give en værdi, aktiveres return_void() metoden. I dette tilfælde er det også en tom funktion.

Vi definerer også tre medlemsfunktioner i 'ThisCorout'. Funktionen await_ready() kaldes for at kontrollere, om coroutinen er klar til at genoptage eksekveringen. I dette eksempel returnerer den altid falsk, hvilket indikerer, at coroutinen ikke er klar til at genoptages med det samme. Når koroutinen skal suspenderes, kaldes metoden await_suspend(). Her er det en tom funktion, hvilket betyder, at der ikke er behov for ophæng. Programmet kalder await_resume() når coroutinen genoptages efter suspension. Det udsender bare en meddelelse, der siger, at coroutinen er blevet genoptaget.

De næste linjer i koden definerer foo() coroutine-funktionen. Inde i foo() begynder vi med at udskrive en besked, der fortæller, at koroutinen er startet. Derefter bruges co_await std::suspend_always{} til at suspendere coroutinen og angiver, at den kan genoptages på et senere tidspunkt. Co_return-sætningen bruges til at afslutte coroutinen uden at returnere nogen værdi.

I main()-funktionen konstruerer vi et objekt 'cr' af typen 'ThisCorout' ved at kalde foo(). Dette skaber og starter coroutinen. Derefter udskrives en meddelelse, der angiver, at coroutinen er blevet oprettet. Dernæst kalder vi await_resume() på 'cr' coroutine-objektet for at genoptage dets eksekvering. Inde i await_resume() udskrives meddelelsen 'The Coroutine is resumed'. Til sidst viser vi en meddelelse, der angiver, at koroutinen er færdig, før programmet afsluttes.

Når du kører dette program, er outputtet som følger:

Eksempel 2: Coroutine med parametre og udbytte

Til denne illustration giver vi nu en kode, der demonstrerer brugen af ​​koroutiner med parametre og udbytte i C++ for at skabe en generatorlignende adfærd til at producere en sekvens af tal.

#include

#inkluder

#inkluder

struktur NYKoroutine {

struktur p_type {

std :: vektor < int > værdier ;

NYCoroutine get_return_object ( ) { Vend tilbage { } ; }

std :: suspend_always initial_suspend ( ) { Vend tilbage { } ; }

std :: suspend_always final_suspend ( ) nej undtagen { Vend tilbage { } ; }

ugyldig unhandled_exception ( ) { }

ugyldig return_void ( ) { }

std :: suspend_always udbytteværdi ( int værdi ) {

værdier. skub tilbage ( værdi ) ;

Vend tilbage { } ;

}

} ;

std :: vektor < int > værdier ;

struktur iterator {

std :: coroutine_handle <> chorus_handle ;

bool operatør != ( konst iterator & Andet ) konst { Vend tilbage chorus_handle != Andet. chorus_handle ; }

iterator & operatør ++ ( ) { chorus_handle. Genoptag ( ) ; Vend tilbage * det her ; }

int operatør * ( ) konst { Vend tilbage chorus_handle. løfte ( ) . værdier [ 0 ] ; }

} ;

iterator begynde ( ) { Vend tilbage iterator { std :: coroutine_handle < p_type >:: fra_løfte ( løfte ( ) ) } ; }

iteratorens ende ( ) { Vend tilbage iterator { nullptr } ; }

std :: coroutine_handle < p_type > løfte ( ) { Vend tilbage
std :: coroutine_handle < p_type >:: fra_løfte ( * det her ) ; }

} ;

NY Coroutine genererer numre ( ) {

co_yield 5 ;

co_yield 6 ;

co_yield 7 ;

}

int vigtigste ( ) {

NY Coroutine nc = generere numre ( ) ;

til ( int værdi : nc ) {

std :: cout << værdi << ' ' ;

}

std :: cout << std :: endl ;

Vend tilbage 0 ;

}

I den forrige kode repræsenterer NEWCoroutine-strukturen en coroutine-baseret generator. Den indeholder en indlejret 'p_type' struktur, der fungerer som løftetypen for coroutinen. p_type-strukturen definerer de funktioner, der kræves af coroutine-maskineriet, såsom get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() og return_void(). p_type-strukturen inkluderer også yield_value(int value)-funktionen, som bruges til at give værdierne fra coroutinen. Det tilføjer den angivne værdi til værdivektoren.

NEWCoroutine-strukturen inkluderer std::vector -medlemsvariablen kaldet 'værdier', som repræsenterer de genererede værdier. Inde i NEWCoroutine er der en indlejret struktur-iterator, der gør det muligt at iterere over de genererede værdier. Den har et coro_handle, som er et håndtag til coroutinen og definerer operatorerne som !=, ++ og * for iteration.

Vi bruger funktionen begin() til at oprette en iterator i starten af ​​coroutinen ved at hente coro_handle fra p_type løftet. Hvorimod end()-funktionen opretter en iterator, der repræsenterer slutningen af ​​coroutinen og er konstrueret med en nullptr coro_handle. Derefter bruges funktionen løfte() til at returnere løftetypen ved at oprette en coroutine_handle fra løftet p_type. GenererNumbers()-funktionen er en coroutine, der giver tre værdier – 5, 6 og 7 – ved hjælp af nøgleordet co_yield.

I main()-funktionen oprettes en forekomst af NEWCoroutine med navnet 'nc' ved at påkalde generNumbers()-coroutinen. Dette initialiserer coroutinen og fanger dens tilstand. En rækkebaseret 'for'-løkke bruges til at iterere over værdierne af 'nc', og hver værdi udskrives, som er adskilt af et mellemrum ved hjælp af std::cout.

Det genererede output er som følger:

Konklusion

Denne artikel demonstrerer brugen af ​​koroutiner i C++. Vi diskuterede to eksempler. Til den første illustration oprettes den grundlæggende koroutine i et C++-program ved hjælp af coroutine-funktionerne. Mens den anden demonstration blev udført ved at bruge koroutinerne med parametre og give efter for at generere en generatorlignende adfærd for at skabe en talsekvens.