Съпрограмите се основават на концепцията за генератори, където дадена функция може да даде стойности и по-късно да бъде възобновена, за да продължи изпълнението. Coroutines осигуряват мощен инструмент за управление на асинхронните операции и могат значително да подобрят цялостното качество на вашия код.
Използване на Coroutines
Корутините са необходими по няколко причини в съвременното програмиране, особено в езици като C++. Ето някои ключови причини, поради които корутините са полезни:
Coroutines осигуряват елегантно решение за асинхронно програмиране. Те правят възможно създаването на код, който изглежда последователен и блокиращ, което е по-лесно за разсъждение и разбиране. Съпрограмите могат да спрат изпълнението си в определени точки, без да блокират нишките, позволявайки паралелна работа на други задачи. Поради това системните ресурси могат да се използват по-ефективно и отзивчивостта се увеличава в приложения, които включват I/O операции или чакат външни събития.
Те могат да направят кода по-лесен за разбиране и поддръжка. Чрез елиминиране на сложните вериги за обратно извикване или държавни машини, съпрограммите позволяват кодът да бъде написан в по-линеен и последователен стил. Това подобрява организацията на кода, намалява влагането и прави логиката лесна за разбиране.
Корутините предоставят структуриран начин за справяне с едновременността и паралелизма. Те ви позволяват да изразите сложните координационни модели и асинхронните работни потоци, като използвате по-интуитивен синтаксис. За разлика от традиционните модели на нишки, при които нишките могат да бъдат блокирани, съпрограммите могат да освободят системните ресурси и да позволят ефективна многозадачност.
Нека създадем няколко примера, за да демонстрираме внедряването на съпрограмми в C++.
Пример 1: Основни корутини
Примерът за основни съпрограмми е даден по-долу:
#include
#include
структура ThisCorout {
структура тип_обещание {
ThisCorout get_return_object ( ) { връщане { } ; }
std :: suspend_never първоначално_спиране ( ) { връщане { } ; }
std :: suspend_never окончателно_спиране ( ) noexcept { връщане { } ; }
невалиден необработено_изключение ( ) { }
невалиден return_void ( ) { }
} ;
bool await_ready ( ) { връщане невярно ; }
невалиден await_suspend ( std :: манипулатор на съпрограма <> ч ) { }
невалиден await_resume ( ) { std :: cout << „Корутината е възобновена.“ << std :: endl ; }
} ;
Това е Corout foo ( ) {
std :: cout << „Корутината започна.“ << std :: endl ;
co_await std :: suspend_always { } ;
съвместно_връщане ;
}
вътр основен ( ) {
Автоматичен кр = Фу ( ) ;
std :: cout << „Корутината е създадена.“ << std :: endl ;
кр. await_resume ( ) ;
std :: cout << „Корутината приключи.“ << std :: endl ;
връщане 0 ;
}
Нека да преминем през предоставения по-рано код и да го обясним подробно:
След като включим необходимите заглавни файлове, ние дефинираме структурата „ThisCorout“, която представлява съпрограма. Вътре в „ThisCorout“ е дефинирана друга структура, която е „promise_type“, която обработва обещанието на съпрограмата. Тази структура предоставя различни функции, които се изискват от сърутинната машина.
Вътре в скобите използваме функцията get_return_object(). Той връща самия обект на съпрограма. В този случай той връща празен обект „ThisCorout“. След това се извиква функцията initial_suspend(), която определя поведението при първото стартиране на съпрограмата. std::suspend_never означава, че съпрограмата не трябва да бъде спряна първоначално.
След това имаме функцията final_suspend(), която определя поведението, когато съпрограмата е на път да приключи. std::suspend_never означава, че съпрограмата не трябва да бъде суспендирана преди нейното финализиране.
Ако съпрограма хвърли изключение, се извиква методът unhandled_exception(). В този пример това е празна функция, но можете да обработвате изключенията според нуждите. Когато съпрограмата приключи, без да даде стойност, се извиква методът return_void(). В този случай това също е празна функция.
Ние също така дефинираме три членски функции в „ThisCorout“. Функцията await_ready() се извиква, за да провери дали съпрограмата е готова да поднови изпълнението. В този пример той винаги връща false, което показва, че съпрограмата не е готова за незабавно възобновяване. Когато съпрограмата ще бъде спряна, се извиква методът await_suspend(). Тук това е празна функция, което означава, че не е необходимо спиране. Програмата извиква await_resume(), когато съпрограмата се възобнови след спиране. Той просто извежда съобщение, което гласи, че съпрограмата е възобновена.
Следващите редове от кода дефинират съпрограмата foo(). Вътре във foo() започваме с отпечатване на съобщение, което гласи, че съпрограмата е стартирана. След това co_await std::suspend_always{} се използва за спиране на съпрограмата и показва, че тя може да бъде възобновена на по-късен етап. Операторът co_return се използва за завършване на съпрограмата без връщане на стойност.
Във функцията main() конструираме обект „cr“ от тип „ThisCorout“ чрез извикване на foo(). Това създава и стартира сърутината. След това се отпечатва съобщение, което гласи, че съпрограмата е създадена. След това извикваме await_resume() на обекта на съпрограмата „cr“, за да възобновим изпълнението му. Вътре в await_resume() се отпечатва съобщението „Съпрограмата е възобновена“. Накрая показваме съобщение, което гласи, че съпрограмата е завършена, преди програмата да приключи.
Когато стартирате тази програма, изходът е както следва:
Пример 2: Съпрограма с параметри и добив
Сега, за тази илюстрация, предоставяме код, който демонстрира използването на съпрограми с параметри и yielding в C++ за създаване на подобно на генератор поведение за създаване на поредица от числа.
#include#include
#include <вектор>
структура НОВА рутина {
структура p_type {
std :: вектор < вътр > стойности ;
НОВАКорутина get_return_object ( ) { връщане { } ; }
std :: suspend_always първоначално_спиране ( ) { връщане { } ; }
std :: suspend_always окончателно_спиране ( ) noexcept { връщане { } ; }
невалиден необработено_изключение ( ) { }
невалиден return_void ( ) { }
std :: suspend_always yield_value ( вътр стойност ) {
стойности. избутвам ( стойност ) ;
връщане { } ;
}
} ;
std :: вектор < вътр > стойности ;
структура итератор {
std :: манипулатор на съпрограма <> chorus_handle ;
bool оператор != ( конст итератор и друго ) конст { връщане chorus_handle != друго. chorus_handle ; }
итератор и оператор ++ ( ) { chorus_handle. продължи ( ) ; връщане * това ; }
вътр оператор * ( ) конст { връщане chorus_handle. обещание ( ) . стойности [ 0 ] ; }
} ;
начало на итератора ( ) { връщане итератор { std :: манипулатор на съпрограма < p_type >:: от_обещание ( обещание ( ) ) } ; }
край на итератора ( ) { връщане итератор { nullptr } ; }
std :: манипулатор на съпрограма < p_type > обещание ( ) { връщане
std :: манипулатор на съпрограма < p_type >:: от_обещание ( * това ) ; }
} ;
NEWCoroutine generateNumbers ( ) {
co_yield 5 ;
co_yield 6 ;
co_yield 7 ;
}
вътр основен ( ) {
НОВОCoroutine nc = генериране на числа ( ) ;
за ( вътр стойност : nc ) {
std :: cout << стойност << ' ' ;
}
std :: cout << std :: endl ;
връщане 0 ;
}
В предишния код структурата NEWCoroutine представлява базиран на съпрограма генератор. Той съдържа вложена структура „p_type“, която служи като обещаващ тип за съпрограмата. Структурата p_type дефинира функциите, които се изискват от сърутинната машина, като get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() и return_void(). Структурата p_type също включва функцията yield_value(int value), която се използва за получаване на стойностите от съпрограмата. Той добавя предоставената стойност към вектора на стойностите.
Структурата NEWCoroutine включва членската променлива std::vector
Използваме функцията begin(), за да създадем итератор в началото на съпрограмата, като получим coro_handle от обещанието p_type. Докато функцията end() създава итератор, който представлява края на съпрограмата и е конструиран с nullptr coro_handle. След това функцията promise() се използва за връщане на типа обещание чрез създаване на coroutine_handle от обещанието p_type. Функцията generateNumbers() е съпрограма, която дава три стойности – 5, 6 и 7 – с помощта на ключовата дума co_yield.
Във функцията main() се създава екземпляр на NEWCoroutine с име „nc“ чрез извикване на съпрограмата generateNumbers(). Това инициализира съпрограмата и улавя нейното състояние. Използва се цикъл „for“, базиран на диапазон, за итериране на стойностите на „nc“ и всяка стойност се отпечатва, които са разделени с интервал, използвайки std::cout.
Генерираният изход е както следва:
Заключение
Тази статия демонстрира използването на съпрограмми в C++. Обсъдихме два примера. За първата илюстрация основната съпрограма е създадена в C++ програма с помощта на функциите на съпрограмата. Докато втората демонстрация беше извършена чрез използване на съпрограми с параметри и отдаване за генериране на подобно на генератор поведение за създаване на поредица от числа.