C++ Coroutines דוגמאות

C Coroutines Dwgm Wt



Coroutines מספקים תכונת שפה המאפשרת לך לכתוב את הקוד האסינכרוני בצורה מאורגנת וליניארית יותר, תוך קידום גישה מובנית ורציפה. הם נותנים מנגנון להשהות ולהפעיל מחדש את ההפעלה של פונקציה במקרים מסוימים מבלי לעצור את כל השרשור. Coroutines מועילים בטיפול במשימות הדורשות המתנה לפעולות I/O כגון קריאה מקובץ או שליחת שיחת רשת.

Coroutines מבוססים על הרעיון של גנרטורים שבהם פונקציה יכולה להניב ערכים ולאחר מכן לחדש את המשך הביצוע. Coroutines מספקים כלי רב עוצמה לניהול הפעולות הא-סינכרוניות ויכולים לשפר מאוד את האיכות הכוללת של הקוד שלך.

שימושים בקורוטינים

Coroutines נחוצים מכמה סיבות בתכנות מודרניות, במיוחד בשפות כמו C++. להלן כמה סיבות עיקריות מדוע קורוטינים מועילים:







Coroutines מספקים פתרון אלגנטי לתכנות אסינכרוני. הם מאפשרים ליצור קוד שנראה רציף וחוסם, שקל יותר לחשוב עליו ולהבין אותו. Coroutines יכולים להשעות את ביצועם בנקודות ספציפיות מבלי לחסום את השרשורים, מה שמאפשר פעולה מקבילה של משימות אחרות. בשל כך, משאבי המערכת עשויים לשמש בצורה יעילה יותר, וההיענות מוגברת ביישומים הכוללים את פעולות ה-I/O או המתנה לאירועים חיצוניים.



הם עשויים להקל על ההבנה והתחזוקה של הקוד. על ידי ביטול שרשראות ההתקשרות המורכבות או מכונות המדינה, קורוטינים מאפשרים לכתוב את הקוד בסגנון ליניארי ורציף יותר. זה משפר את ארגון הקוד, מפחית קינון והופך את ההיגיון לקל להבנה.



Coroutines מספקים דרך מובנית להתמודד עם המקבילות והמקבילות. הם מאפשרים לך לבטא את דפוסי התיאום המורכבים וזרימות העבודה האסינכרוניות באמצעות תחביר אינטואיטיבי יותר. שלא כמו מודלים מסורתיים של השרשור שבהם החוטים עשויים להיות חסומים, קורוטינים יכולים לפנות את משאבי המערכת ולאפשר ריבוי משימות יעיל.





בואו ניצור כמה דוגמאות כדי להדגים את היישום של קורוטינים ב-C++.

דוגמה 1: קורוטינים בסיסיים

הדוגמה הבסיסית של קורוטינים מסופקת בחלק הבא:



#include

#include

struct קוראוט הזה {

struct סוג_הבטחה {

ThisCorout get_return_object ( ) { לַחֲזוֹר { } ; }

סטד :: להשעות_לעולם לא initial_suspend ( ) { לַחֲזוֹר { } ; }

סטד :: להשעות_לעולם לא final_suspend ( ) לא למעט { לַחֲזוֹר { } ; }

בָּטֵל unhanded_exception ( ) { }

בָּטֵל return_void ( ) { }

} ;

bool await_ready ( ) { לַחֲזוֹר שֶׁקֶר ; }

בָּטֵל await_suspend ( סטד :: coroutine_handle <> ח ) { }

בָּטֵל await_resume ( ) { סטד :: cout << 'הקורוטין מתחדש'. << סטד :: endl ; }

} ;

זה קוראוט פו ( ) {

סטד :: cout << 'הקורוטין התחיל'. << סטד :: endl ;

co_wait std :: להשעות_תמיד { } ;

co_return ;

}

int רָאשִׁי ( ) {

אוטומטי cr = פו ( ) ;

סטד :: cout << 'הקורוטין נוצר.' << סטד :: endl ;

cr. await_resume ( ) ;

סטד :: cout << 'קורוטין סיימה.' << סטד :: endl ;

לַחֲזוֹר 0 ;

}

בואו נעבור על הקוד שסופק קודם ונסביר אותו בפירוט:

לאחר הכללת קבצי הכותרות הנדרשים, אנו מגדירים את מבנה ה-'ThisCorout' המייצג קורוטינה. בתוך ה-'ThisCorout', מוגדר מבנה נוסף שהוא 'promise_type' שמטפל בהבטחה Coroutine. מבנה זה מספק פונקציות שונות הנדרשות על ידי מכונות הקורוטינה.

בתוך הסוגריים, אנו משתמשים בפונקציה get_return_object() . זה מחזיר את האובייקט הקורוטי בעצמו. במקרה זה, הוא מחזיר אובייקט 'ThisCorout' ריק. לאחר מכן, הפונקציה initial_suspend() מופעלת אשר קובעת את ההתנהגות כאשר ה-coroutine מופעל לראשונה. ה-std::suspend_never אומר שאין להשעות את ה-coroutine בתחילה.

לאחר מכן, יש לנו את הפונקציה final_suspend() שקובעת את ההתנהגות כאשר ה-coroutine עומד להסתיים. ה-std::suspend_never אומר שאין להשעות את ה-coroutine לפני סיומו.

אם Coroutine זורק חריג, השיטה unhandled_exception() מופעלת. בדוגמה זו, זו פונקציה ריקה, אבל אתה יכול לטפל בחריגים לפי הצורך. כאשר ה-coroutine מסתיימת מבלי להניב ערך, מופעלת השיטה return_void()‎. במקרה זה, זו גם פונקציה ריקה.

אנו גם מגדירים שלוש פונקציות איברים בתוך 'ThisCorout'. הפונקציה await_ready() נקראת כדי לבדוק אם ה-coroutine מוכן לחדש את הביצוע. בדוגמה זו, הוא תמיד מחזיר false מה שמצביע על כך שה-coroutine לא מוכן לחדש באופן מיידי. כאשר ה-coroutine עומד להיות מושעה, השיטה await_suspend() נקראת. כאן, זו פונקציה ריקה שמשמעותה שאין צורך בהשעיה. התוכנית קוראת ל-await_resume() כאשר ה-coroutine מתחדש לאחר ההשעיה. זה רק מוציא הודעה שמציינת שהקוראוטינה חודשה.

השורות הבאות של הקוד מגדירות את הפונקציה foo() coroutine. בתוך foo(), אנו מתחילים בהדפסת הודעה המציינת שהקורוטינה התחילה. לאחר מכן, co_await std::suspend_always{} משמש כדי להשעות את ה-coroutine ומציין שניתן לחדש אותה בשלב מאוחר יותר. ההצהרה co_return משמשת כדי לסיים את הקורוטין מבלי להחזיר ערך כלשהו.

בפונקציה main() אנו בונים אובייקט 'cr' מסוג 'ThisCorout' על ידי קריאה foo(). זה יוצר ומתחיל את הקורוטינה. לאחר מכן, מודפסת הודעה המציינת שהקורוטינה נוצרה. לאחר מכן, אנו קוראים ל-await_resume() באובייקט הקורוטיין 'cr' כדי לחדש את ביצועו. בתוך ה-aawait_resume(), מודפסת ההודעה 'The Coroutine is resumed'. לבסוף, אנו מציגים הודעה שמציינת שהקורוטינה הושלמה לפני שהתוכנית מסתיימת.

כאשר אתה מפעיל תוכנית זו, הפלט הוא כדלקמן:

דוגמה 2: Coroutine עם פרמטרים ותפוקה

כעת, להמחשה זו, אנו מספקים קוד המדגים את השימוש בקורוטינים עם פרמטרים ותפוקה ב-C++ כדי ליצור התנהגות דמוית מחולל להפקת רצף של מספרים.

#include

#include

#include

struct NEWCoroutine {

struct p_type {

סטד :: וֶקטוֹר < int > ערכים ;

NEWCoroutine get_return_object ( ) { לַחֲזוֹר { } ; }

סטד :: להשעות_תמיד initial_suspend ( ) { לַחֲזוֹר { } ; }

סטד :: להשעות_תמיד final_suspend ( ) לא למעט { לַחֲזוֹר { } ; }

בָּטֵל unhanded_exception ( ) { }

בָּטֵל return_void ( ) { }

סטד :: להשעות_תמיד ערך_תשואה ( int ערך ) {

ערכים. התנגדות ( ערך ) ;

לַחֲזוֹר { } ;

}

} ;

סטד :: וֶקטוֹר < int > ערכים ;

struct איטרטור {

סטד :: coroutine_handle <> chorus_handle ;

מפעיל bool != ( const איטרטור & אַחֵר ) const { לַחֲזוֹר chorus_handle != אַחֵר. chorus_handle ; }

איטרטור & מַפעִיל ++ ( ) { chorus_handle. קורות חיים ( ) ; לַחֲזוֹר * זֶה ; }

int מַפעִיל * ( ) const { לַחֲזוֹר chorus_handle. הַבטָחָה ( ) . ערכים [ 0 ] ; }

} ;

איטרטור מתחיל ( ) { לַחֲזוֹר איטרטור { סטד :: coroutine_handle < p_type >:: מ_מבטיח ( הַבטָחָה ( ) ) } ; }

סוף איטרטור ( ) { לַחֲזוֹר איטרטור { nullptr } ; }

סטד :: coroutine_handle < p_type > הַבטָחָה ( ) { לַחֲזוֹר
סטד :: coroutine_handle < p_type >:: מ_מבטיח ( * זֶה ) ; }

} ;

NEWCoroutine יוצר מספרים ( ) {

שיתוף_תשואה 5 ;

שיתוף_תשואה 6 ;

שיתוף_תשואה 7 ;

}

int רָאשִׁי ( ) {

NEWCoroutine nc = ליצור מספרים ( ) ;

ל ( int ערך : nc ) {

סטד :: cout << ערך << '' ;

}

סטד :: cout << סטד :: endl ;

לַחֲזוֹר 0 ;

}

בקוד הקודם, מבנה NEWCoroutine מייצג מחולל מבוסס קורוטינים. הוא מכיל מבנה 'p_type' מקונן המשמש כסוג ההבטחה עבור ה-coroutine. מבנה ה-p_type מגדיר את הפונקציות הנדרשות על ידי מנגנון ה-coroutine כגון get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() ו- return_void(). המבנה p_type כולל גם את הפונקציה yield_value(int value) המשמשת להפקת הערכים מהקורוטין. זה מוסיף את הערך שסופק לווקטור הערכים.

מבנה NEWCoroutine כולל את משתנה האיבר std::vector הנקרא 'ערכים' המייצג את הערכים שנוצרו. בתוך ה-NEWCoroutine, יש איטרטור מבנה מקונן המאפשר לחזור על הערכים שנוצרו. הוא מחזיק coro_handle שהוא ידית ל-coroutine ומגדיר את האופרטורים כגון !=, ++ ו-* עבור איטרציה.

אנו משתמשים בפונקציה begin() כדי ליצור איטרטור בתחילת ה-coroutine על ידי השגת ה-coro_handle מההבטחה p_type. ואילו הפונקציה end() יוצרת איטרטור המייצג את סוף הקורוטין ובנוי עם nullptr coro_handle. לאחר מכן, הפונקציה belofte() מנוצלת כדי להחזיר את סוג ההבטחה על ידי יצירת coroutine_handle מההבטחה p_type. הפונקציה generNumbers() היא קוראוטינה שמניבה שלושה ערכים - 5, 6 ו-7 - באמצעות מילת המפתח co_yield.

בפונקציה main() נוצר מופע של NEWCoroutine בשם 'nc' על ידי הפעלת ה-generNumbers() coroutine. זה מאתחל את הקורוטינה ותופס את מצבה. לולאת 'for' מבוססת טווח משמשת לחזרה על הערכים של 'nc', וכל ערך מודפס ומופרדים ברווח באמצעות ה-std::cout.

הפלט שנוצר הוא כדלקמן:

סיכום

מאמר זה מדגים את השימוש בקורוטינים ב-C++. דנו בשתי דוגמאות. עבור ההמחשה הראשונה, הקורוטין הבסיסי נוצר בתוכנית C++ תוך שימוש בפונקציות הקורוטין. בעוד שההדגמה השנייה בוצעה על ידי ניצול הקורוטינות עם פרמטרים ומתנופה ליצירת התנהגות דמוית מחולל ליצירת רצף של מספרים.