Mga Halimbawa ng C++ Coroutine

Mga Halimbawa Ng C Coroutine



Nagbibigay ang Coroutine ng feature ng wika na nagbibigay-daan sa iyong isulat ang asynchronous na code sa mas organisado at linear na paraan, na nagpo-promote ng structured at sequential na diskarte. Nagbibigay sila ng mekanismo para i-pause at i-restart ang pagpapatupad ng isang function sa mga partikular na pagkakataon nang hindi humihinto sa buong thread. Nakatutulong ang mga coroutine kapag humahawak ng mga gawain na nangangailangan ng paghihintay para sa mga operasyon ng I/O gaya ng pagbabasa mula sa isang file o pagpapadala ng tawag sa network.

Ang mga coroutine ay batay sa konsepto ng mga generator kung saan ang isang function ay maaaring magbunga ng mga halaga at sa ibang pagkakataon ay maipagpatuloy upang ipagpatuloy ang pagpapatupad. Ang mga Coroutine ay nagbibigay ng isang mahusay na tool upang pamahalaan ang mga asynchronous na operasyon at maaaring lubos na mapabuti ang pangkalahatang kalidad ng iyong code.

Paggamit ng Coroutines

Ang mga coroutine ay kailangan para sa ilang kadahilanan sa modernong programming, partikular sa mga wika tulad ng C++. Narito ang ilang pangunahing dahilan kung bakit kapaki-pakinabang ang mga coroutine:







Nagbibigay ang mga Coroutine ng eleganteng solusyon sa asynchronous na programming. Ginagawa nilang posible na lumikha ng isang code na lumilitaw na sunud-sunod at pagharang na mas madaling mangatuwiran at maunawaan. Maaaring suspindihin ng mga Coroutine ang kanilang pagpapatupad sa mga partikular na punto nang hindi hinaharangan ang mga thread, na nagpapagana ng parallel na operasyon ng iba pang mga gawain. Dahil dito, ang mga mapagkukunan ng system ay maaaring magamit nang mas epektibo, at ang kakayahang tumugon ay tumaas sa mga application na may kinalaman sa mga operasyon ng I/O o naghihintay para sa mga panlabas na kaganapan.



Maaari nilang gawing mas madaling maunawaan at mapanatili ang code. Sa pamamagitan ng pag-aalis sa mga kumplikadong callback chain o state machine, pinapagana ng mga coroutine ang code na maisulat sa mas linear at sequential na istilo. Pinapabuti nito ang organisasyon ng code, binabawasan ang nesting, at ginagawang madaling maunawaan ang lohika.



Ang mga Coroutine ay nagbibigay ng isang structured na paraan upang pangasiwaan ang concurrency at parallelism. Nagbibigay-daan sa iyo ang mga ito na ipahayag ang kumplikadong mga pattern ng koordinasyon at mga asynchronous na daloy ng trabaho gamit ang isang mas intuitive na syntax. Hindi tulad ng mga tradisyonal na modelo ng threading kung saan maaaring ma-block ang mga thread, maaaring palayain ng mga coroutine ang mga mapagkukunan ng system at paganahin ang isang mahusay na multitasking.





Gumawa tayo ng ilang halimbawa para ipakita ang pagpapatupad ng mga coroutine sa C++.

Halimbawa 1: Mga Pangunahing Coroutine

Ang pangunahing halimbawa ng coroutine ay ibinigay sa sumusunod:



#include

#include

struct ThisCorout {

struct promise_type {

ThisCorout get_return_object ( ) { bumalik { } ; }

std :: suspend_never initial_suspend ( ) { bumalik { } ; }

std :: suspend_never final_suspend ( ) noexcept { bumalik { } ; }

walang bisa unhandled_exception ( ) { }

walang bisa return_void ( ) { }

} ;

bool wait_ready ( ) { bumalik mali ; }

walang bisa wait_suspend ( std :: coroutine_handle <> h ) { }

walang bisa wait_resume ( ) { std :: cout << 'Ang Coroutine ay ipinagpatuloy.' << std :: endl ; }

} ;

ThisCorout foo ( ) {

std :: cout << 'Nagsimula na ang Coroutine.' << std :: endl ;

co_await std :: suspend_always { } ;

co_return ;

}

int pangunahing ( ) {

sasakyan cr = foo ( ) ;

std :: cout << 'Ang Coroutine ay nilikha.' << std :: endl ;

cr. wait_resume ( ) ;

std :: cout << 'Tapos na ang Coroutine.' << std :: endl ;

bumalik 0 ;

}

Tingnan natin ang dating ibinigay na code at ipaliwanag ito nang detalyado:

Pagkatapos isama ang mga kinakailangang file ng header, tinukoy namin ang istrukturang 'ThisCorout' na kumakatawan sa isang coroutine. Sa loob ng 'ThisCorout', isa pang struct na 'promise_type' ang tinukoy na humahawak sa coroutine promise. Ang istrukturang ito ay nagbibigay ng iba't ibang mga function na kinakailangan ng coroutine machinery.

Sa loob ng mga bracket, ginagamit namin ang get_return_object() function. Ibinabalik nito ang mismong coroutine object. Sa pagkakataong ito, nagbabalik ito ng walang laman na bagay na 'ThisCorout'. Pagkatapos, ang initial_suspend() function ay ginagamit na tumutukoy sa gawi kapag ang coroutine ay unang nagsimula. Ang std::suspend_never ay nangangahulugan na ang coroutine ay hindi dapat masuspinde sa simula.

Pagkatapos nito, mayroon kaming function na final_suspend() na tumutukoy sa gawi kapag malapit nang matapos ang coroutine. Ang std::suspend_never ay nangangahulugan na ang coroutine ay hindi dapat masuspinde bago ang pagsasapinal nito.

Kung ang isang coroutine ay naghagis ng isang exception, ang unhandled_exception() na pamamaraan ay ginagamit. Sa halimbawang ito, ito ay isang walang laman na function, ngunit maaari mong pangasiwaan ang mga pagbubukod kung kinakailangan. Kapag natapos ang coroutine nang hindi nagbubunga ng halaga, ang return_void() na paraan ay ginagamit. Sa kasong ito, isa rin itong walang laman na function.

Tinukoy din namin ang tatlong function ng miyembro sa loob ng 'ThisCorout'. Ang await_ready() function ay tinatawag upang suriin kung ang coroutine ay handa na upang ipagpatuloy ang pagpapatupad. Sa halimbawang ito, palagi itong nagbabalik ng false na nagpapahiwatig na ang coroutine ay hindi pa handang ipagpatuloy kaagad. Kapag ang coroutine ay sususpindihin, ang paraang await_suspend() ay tinatawag. Dito, isa itong walang laman na function na nangangahulugan na walang suspensiyon ang kailangan. Ang programa ay tumatawag sa await_resume() kapag ang coroutine ay ipinagpatuloy pagkatapos ng pagsususpinde. Naglalabas lamang ito ng mensahe na nagsasaad na ang coroutine ay naipagpatuloy.

Ang mga susunod na linya ng code ay tumutukoy sa foo() coroutine function. Sa loob ng foo(), magsisimula tayo sa pamamagitan ng pag-print ng mensahe na nagsasaad na nagsimula na ang coroutine. Pagkatapos, ang co_await std::suspend_always{} ay ginagamit upang suspindihin ang coroutine at ipinapahiwatig na maaari itong ipagpatuloy sa ibang pagkakataon. Ang co_return statement ay ginagamit upang tapusin ang coroutine nang hindi nagbabalik ng anumang halaga.

Sa main() function, gumagawa kami ng object na 'cr' ng uri na 'ThisCorout' sa pamamagitan ng pagtawag sa foo(). Lumilikha ito at magsisimula ng coroutine. Pagkatapos, ang isang mensahe na nagsasaad na ang coroutine ay nilikha ay naka-print. Susunod, tinatawagan namin ang await_resume() sa “cr” coroutine object upang ipagpatuloy ang pagpapatupad nito. Sa loob ng await_resume(), naka-print ang mensaheng 'The Coroutine is resumed'. Panghuli, nagpapakita kami ng mensaheng nagsasaad na kumpleto na ang coroutine bago matapos ang program.

Kapag pinatakbo mo ang program na ito, ang output ay ang mga sumusunod:

Halimbawa 2: Coroutine na may Mga Parameter at Pagbibigay

Ngayon, para sa paglalarawang ito, nagbibigay kami ng isang code na nagpapakita ng paggamit ng mga coroutine na may mga parameter at nagbubunga sa C++ upang lumikha ng isang pag-uugaling tulad ng generator upang makagawa ng pagkakasunod-sunod ng mga numero.

#include

#include

#include

struct BAGONGCoroutine {

struct p_type {

std :: vector < int > mga halaga ;

NEWCoroutine get_return_object ( ) { bumalik { } ; }

std :: suspend_always initial_suspend ( ) { bumalik { } ; }

std :: suspend_always final_suspend ( ) noexcept { bumalik { } ; }

walang bisa unhandled_exception ( ) { }

walang bisa return_void ( ) { }

std :: suspend_always yield_value ( int halaga ) {

mga halaga. push_back ( halaga ) ;

bumalik { } ;

}

} ;

std :: vector < int > mga halaga ;

struct umuulit {

std :: coroutine_handle <> chorus_handle ;

bool operator != ( const umuulit at iba pa ) const { bumalik chorus_handle != iba pa. chorus_handle ; }

umuulit at operator ++ ( ) { chorus_handle. ipagpatuloy ( ) ; bumalik * ito ; }

int operator * ( ) const { bumalik chorus_handle. pangako ( ) . mga halaga [ 0 ] ; }

} ;

magsisimula ang iterator ( ) { bumalik umuulit { std :: coroutine_handle < p_type >:: mula sa_pangako ( pangako ( ) ) } ; }

pagtatapos ng iterator ( ) { bumalik umuulit { nullptr } ; }

std :: coroutine_handle < p_type > pangako ( ) { bumalik
std :: coroutine_handle < p_type >:: mula sa_pangako ( * ito ) ; }

} ;

NEWCoroutine generateNumbers ( ) {

co_yield 5 ;

co_yield 6 ;

co_yield 7 ;

}

int pangunahing ( ) {

BAGONGCoroutine nc = generateNumbers ( ) ;

para sa ( int halaga : nc ) {

std :: cout << halaga << '' ;

}

std :: cout << std :: endl ;

bumalik 0 ;

}

Sa nakaraang code, ang NEWCoroutine struct ay kumakatawan sa isang coroutine-based generator. Naglalaman ito ng nested na 'p_type' na struct na nagsisilbing promise type para sa coroutine. Ang p_type struct ay tumutukoy sa mga function na kinakailangan ng coroutine machinery gaya ng get_return_object(), initial_suspend(), final_suspend(), unhandled_exception(), at return_void(). Kasama rin sa p_type struct ang yield_value(int value) function na ginagamit upang ibigay ang mga value mula sa coroutine. Idinaragdag nito ang ibinigay na halaga sa vector ng mga halaga.

Kasama sa NEWCoroutine struct ang std::vector member variable na tinatawag na “values” na kumakatawan sa mga nabuong value. Sa loob ng NEWCoroutine, mayroong isang nested struct iterator na nagbibigay-daan sa pag-ulit sa mga nabuong halaga. May hawak itong coro_handle na isang hawakan sa coroutine at tinutukoy ang mga operator tulad ng !=, ++, at * para sa pag-ulit.

Ginagamit namin ang begin() function upang lumikha ng isang iterator sa simula ng coroutine sa pamamagitan ng pagkuha ng coro_handle mula sa p_type na pangako. Samantalang ang end() function ay lumilikha ng isang iterator na kumakatawan sa dulo ng coroutine at binuo gamit ang isang nullptr coro_handle. Pagkatapos nito, ang promise() function ay ginagamit upang ibalik ang promise type sa pamamagitan ng paggawa ng coroutine_handle mula sa p_type promise. Ang generateNumbers() function ay isang coroutine na nagbubunga ng tatlong value – 5, 6, at 7 – gamit ang co_yield na keyword.

Sa main() function, isang instance ng NEWCoroutine na pinangalanang 'nc' ay nilikha sa pamamagitan ng paggamit ng generateNumbers() coroutine. Sinisimulan nito ang coroutine at kinukuha ang estado nito. Ginagamit ang isang loop na nakabatay sa saklaw na 'para sa' upang umulit sa mga halaga ng 'nc', at ang bawat halaga ay naka-print na pinaghihiwalay ng isang puwang gamit ang std::cout.

Ang nabuong output ay ang mga sumusunod:

Konklusyon

Ipinapakita ng artikulong ito ang paggamit ng mga coroutine sa C++. Tinalakay namin ang dalawang halimbawa. Para sa unang paglalarawan, ang pangunahing coroutine ay nilikha sa isang C++ na programa gamit ang mga function ng coroutine. Habang ang pangalawang demonstrasyon ay isinagawa sa pamamagitan ng paggamit ng mga coroutine na may mga parameter at nagbubunga upang makabuo ng isang generator-like na pag-uugali upang lumikha ng isang sequence ng mga numero.