C++ Coroutines کی مثالیں۔

C Coroutines Ky Mthaly



Coroutines ایک زبان کی خصوصیت فراہم کرتی ہے جو آپ کو ایک منظم اور ترتیب وار انداز کو فروغ دیتے ہوئے، زیادہ منظم اور لکیری انداز میں غیر مطابقت پذیر کوڈ لکھنے کے قابل بناتی ہے۔ وہ پورے تھریڈ کو روکے بغیر کسی فنکشن کے عمل کو روکنے اور دوبارہ شروع کرنے کا طریقہ کار دیتے ہیں۔ کورٹینز ایسے کاموں کو سنبھالنے میں مددگار ثابت ہوتی ہیں جن کے لیے I/O آپریشنز کا انتظار کرنا پڑتا ہے جیسے کہ فائل سے پڑھنا یا نیٹ ورک کال بھیجنا۔

Coroutines جنریٹرز کے تصور پر مبنی ہیں جہاں ایک فنکشن اقدار حاصل کر سکتا ہے اور بعد میں عملدرآمد کو جاری رکھنے کے لیے دوبارہ شروع کیا جا سکتا ہے۔ Coroutines غیر مطابقت پذیر کارروائیوں کو منظم کرنے کے لیے ایک طاقتور ٹول فراہم کرتے ہیں اور آپ کے کوڈ کے مجموعی معیار کو بہت بہتر بنا سکتے ہیں۔

Coroutines کے استعمال

جدید پروگرامنگ میں خاص طور پر C++ جیسی زبانوں میں کئی وجوہات کی بناء پر Coroutines کی ضرورت ہوتی ہے۔ یہاں کچھ اہم وجوہات ہیں کہ کوروٹینز کیوں فائدہ مند ہیں:







Coroutines غیر مطابقت پذیر پروگرامنگ کا ایک خوبصورت حل فراہم کرتے ہیں۔ وہ ایک ایسا کوڈ بنانا ممکن بناتے ہیں جو ترتیب وار اور مسدود ہو جس کے بارے میں استدلال اور سمجھنا آسان ہو۔ Coroutines دھاگوں کو مسدود کیے بغیر مخصوص پوائنٹس پر اپنے عمل کو معطل کر سکتے ہیں، دوسرے کاموں کے متوازی آپریشن کو فعال کر سکتے ہیں۔ اس کی وجہ سے، سسٹم کے وسائل کو زیادہ مؤثر طریقے سے استعمال کیا جا سکتا ہے، اور ان ایپلی کیشنز میں ردعمل میں اضافہ ہوتا ہے جن میں I/O آپریشنز شامل ہوتے ہیں یا بیرونی واقعات کا انتظار کرتے ہیں۔



وہ کوڈ کو سمجھنے اور برقرار رکھنے میں آسانی پیدا کر سکتے ہیں۔ پیچیدہ کال بیک زنجیروں یا ریاستی مشینوں کو ختم کرکے، کوروٹینز کوڈ کو زیادہ لکیری اور ترتیب وار انداز میں لکھنے کے قابل بناتے ہیں۔ یہ کوڈ کی تنظیم کو بہتر بناتا ہے، گھونسلے کو کم کرتا ہے، اور منطق کو سمجھنے میں آسان بناتا ہے۔



Coroutines ہم آہنگی اور ہم آہنگی کو سنبھالنے کا ایک منظم طریقہ فراہم کرتے ہیں۔ وہ آپ کو زیادہ بدیہی نحو کا استعمال کرتے ہوئے پیچیدہ کوآرڈینیشن پیٹرن اور غیر مطابقت پذیر ورک فلو کا اظہار کرنے کی اجازت دیتے ہیں۔ روایتی تھریڈنگ ماڈلز کے برعکس جہاں تھریڈز کو بلاک کیا جا سکتا ہے، کوروٹینز سسٹم کے وسائل کو آزاد کر سکتے ہیں اور ایک موثر ملٹی ٹاسکنگ کو فعال کر سکتے ہیں۔





آئیے C++ میں کوروٹینز کے نفاذ کو ظاہر کرنے کے لیے کچھ مثالیں بناتے ہیں۔

مثال 1: بنیادی کوروٹائنز

بنیادی کوروٹینز کی مثال درج ذیل میں فراہم کی گئی ہے:



# شامل کریں

#include

ساخت یہ کوروٹ {

ساخت وعدہ_قسم {

ThisCorout get_return_object ( ) { واپسی { } ; }

std :: معطل_کبھی نہیں ابتدائی_معطل ( ) { واپسی { } ; }

std :: معطل_کبھی نہیں حتمی_معطل ( ) اس کے علاوہ { واپسی { } ; }

باطل unhandled_exception ( ) { }

باطل واپسی_باطل ( ) { }

} ;

bool await_ready ( ) { واپسی جھوٹا ; }

باطل await_suspend ( std :: coroutine_handle <> h ) { }

باطل await_resume ( ) { std :: cout << 'کوروٹائن دوبارہ شروع ہو گئی ہے۔' << std :: endl ; }

} ;

ThisCorout foo ( ) {

std :: cout << 'کوروٹین شروع ہو گئی ہے۔' << std :: endl ;

co_await std :: ہمیشہ معطل کریں۔ { } ;

co_return ;

}

int مرکزی ( ) {

آٹو cr = foo ( ) ;

std :: cout << 'کوروٹین بنائی گئی ہے۔' << std :: endl ;

cr await_resume ( ) ;

std :: cout << 'کوروٹین ختم۔' << std :: endl ;

واپسی 0 ;

}

آئیے پہلے فراہم کردہ کوڈ کو دیکھیں اور اس کی تفصیل سے وضاحت کریں:

مطلوبہ ہیڈر فائلوں کو شامل کرنے کے بعد، ہم 'ThisCorout' ڈھانچہ کی وضاحت کرتے ہیں جو کورٹین کی نمائندگی کرتا ہے۔ 'ThisCorout' کے اندر، ایک اور ڈھانچہ جو کہ 'promise_type' ہے اس کی تعریف کی گئی ہے جو کورٹین وعدے کو سنبھالتی ہے۔ یہ ڈھانچہ مختلف افعال فراہم کرتا ہے جو کورٹین مشینری کو درکار ہوتے ہیں۔

بریکٹ کے اندر، ہم get_return_object() فنکشن کا استعمال کرتے ہیں۔ یہ خود کوروٹین آبجیکٹ کو لوٹاتا ہے۔ اس مثال میں، یہ ایک خالی 'ThisCorout' آبجیکٹ لوٹاتا ہے۔ اس کے بعد، ابتدائی_سسپنڈ() فنکشن کو طلب کیا جاتا ہے جو روٹین کا تعین کرتا ہے جب کورٹین پہلی بار شروع کی جاتی ہے۔ std::suspend_never کا مطلب ہے کہ کوروٹین کو ابتدائی طور پر معطل نہیں کیا جانا چاہیے۔

اس کے بعد، ہمارے پاس final_suspend() فنکشن ہے جو روٹین کا تعین کرتا ہے جب کورٹین ختم ہونے والی ہے۔ std::suspend_never کا مطلب یہ ہے کہ کوروٹین کو حتمی شکل دینے سے پہلے معطل نہیں کیا جانا چاہیے۔

اگر کوئی کوروٹین استثناء دیتا ہے، تو unhandled_exception() طریقہ استعمال کیا جاتا ہے۔ اس مثال میں، یہ ایک خالی فنکشن ہے، لیکن آپ ضرورت کے مطابق مستثنیات کو سنبھال سکتے ہیں۔ جب کوروٹین کوئی قدر حاصل کیے بغیر ختم ہو جاتی ہے، تو return_void() طریقہ استعمال کیا جاتا ہے۔ اس صورت میں، یہ ایک خالی فنکشن بھی ہے۔

ہم 'ThisCorout' کے اندر تین ممبر فنکشنز کی بھی وضاحت کرتے ہیں۔ await_ready() فنکشن کو یہ چیک کرنے کے لیے بلایا جاتا ہے کہ آیا کورٹین دوبارہ شروع کرنے کے لیے تیار ہے۔ اس مثال میں، یہ ہمیشہ غلط لوٹتا ہے جو اس بات کی نشاندہی کرتا ہے کہ کوروٹین فوری طور پر دوبارہ شروع ہونے کے لیے تیار نہیں ہے۔ جب کوروٹین معطل ہونے والا ہے، طریقہ await_suspend() کہا جاتا ہے۔ یہاں، یہ ایک خالی فنکشن ہے جس کا مطلب ہے کہ کوئی معطلی ضروری نہیں ہے۔ پروگرام await_resume() کو کال کرتا ہے جب کوروٹین کو معطلی کے بعد دوبارہ شروع کیا جاتا ہے۔ یہ صرف ایک پیغام دیتا ہے جس میں کہا گیا ہے کہ کوروٹین دوبارہ شروع کر دی گئی ہے۔

کوڈ کی اگلی لائنیں foo() کوروٹین فنکشن کی وضاحت کرتی ہیں۔ foo() کے اندر، ہم ایک پیغام پرنٹ کرکے شروع کرتے ہیں جس میں کہا گیا ہے کہ کورٹین شروع ہو گئی ہے۔ پھر، co_await std::suspend_always{} کا استعمال کورٹین کو معطل کرنے کے لیے کیا جاتا ہے اور اشارہ کرتا ہے کہ اسے بعد میں دوبارہ شروع کیا جا سکتا ہے۔ co_return کا بیان بغیر کسی قدر واپس کیے کورٹین کو ختم کرنے کے لیے استعمال کیا جاتا ہے۔

مین () فنکشن میں، ہم foo() کو کال کر کے 'ThisCorout' قسم کی ایک آبجیکٹ 'cr' بناتے ہیں۔ یہ کورٹین بناتا اور شروع کرتا ہے۔ اس کے بعد، ایک پیغام جس میں کہا گیا ہے کہ کوروٹین بنایا گیا ہے پرنٹ کیا جاتا ہے۔ اس کے بعد، ہم await_resume() کو 'cr' کورٹین آبجیکٹ پر کال کرتے ہیں تاکہ اس پر عمل درآمد دوبارہ شروع کیا جا سکے۔ await_resume() کے اندر، 'The Coroutine دوبارہ شروع ہو گیا ہے' پیغام پرنٹ ہوتا ہے۔ آخر میں، ہم ایک پیغام ڈسپلے کرتے ہیں جس میں بتایا گیا ہے کہ پروگرام کے ختم ہونے سے پہلے کورٹین مکمل ہو گیا ہے۔

جب آپ اس پروگرام کو چلاتے ہیں تو آؤٹ پٹ اس طرح ہوتا ہے:

مثال 2: پیرامیٹرز اور پیداوار کے ساتھ کورٹین

اب، اس مثال کے لیے، ہم ایک کوڈ فراہم کرتے ہیں جو نمبروں کی ترتیب پیدا کرنے کے لیے جنریٹر جیسا رویہ پیدا کرنے کے لیے پیرامیٹرز کے ساتھ کوروٹائنز کے استعمال اور C++ میں حاصل ہونے کو ظاہر کرتا ہے۔

# شامل کریں

#include

#شامل <ویکٹر>

ساخت نیوکورٹائن {

ساخت p_type {

std :: ویکٹر < int > اقدار ;

NEWCoroutine get_return_object ( ) { واپسی { } ; }

std :: ہمیشہ معطل کریں۔ ابتدائی_معطل ( ) { واپسی { } ; }

std :: ہمیشہ معطل کریں۔ حتمی_معطل ( ) اس کے علاوہ { واپسی { } ; }

باطل unhandled_exception ( ) { }

باطل واپسی_باطل ( ) { }

std :: ہمیشہ معطل کریں۔ yield_value ( int قدر ) {

اقدار پیچھے دھکیلا ( قدر ) ;

واپسی { } ;

}

} ;

std :: ویکٹر < int > اقدار ;

ساخت تکرار کرنے والا {

std :: coroutine_handle <> chorus_handle ;

bool آپریٹر != ( const تکرار کرنے والا اور دوسرے ) const { واپسی chorus_handle != دوسرے chorus_handle ; }

تکرار کرنے والا اور آپریٹر ++ ( ) { chorus_handle دوبارہ شروع کریں ( ) ; واپسی * یہ ; }

int آپریٹر * ( ) const { واپسی chorus_handle وعدہ ( ) . اقدار [ 0 ] ; }

} ;

تکرار شروع ( ) { واپسی تکرار کرنے والا { std :: coroutine_handle < p_type >:: سے_وعدہ ( وعدہ ( ) ) } ; }

تکرار کرنے والا اختتام ( ) { واپسی تکرار کرنے والا { nullptr } ; }

std :: coroutine_handle < p_type > وعدہ ( ) { واپسی
std :: coroutine_handle < p_type >:: سے_وعدہ ( * یہ ) ; }

} ;

NEWCoroutine generateNumbers ( ) {

co_yield 5 ;

co_yield 6 ;

co_yield 7 ;

}

int مرکزی ( ) {

NEWCoroutine NC = نمبر تیار کریں۔ ( ) ;

کے لیے ( int قدر : nc ) {

std :: cout << قدر << ' ;

}

std :: cout << std :: endl ;

واپسی 0 ;

}

پچھلے کوڈ میں، NEWCoroutine struct ایک کوروٹائن پر مبنی جنریٹر کی نمائندگی کرتا ہے۔ اس میں نیسٹڈ 'p_type' ڈھانچہ ہوتا ہے جو کورٹین کے لیے وعدے کی قسم کے طور پر کام کرتا ہے۔ p_type struct ان فنکشنز کی وضاحت کرتا ہے جو کورٹین مشینری کے لیے درکار ہوتے ہیں جیسے get_return_object(), initial_suspend(), final_suspend(), unhandled_exception(), اور return_void()۔ p_type struct میں yield_value(int value) فنکشن بھی شامل ہوتا ہے جو کورٹین سے اقدار حاصل کرنے کے لیے استعمال ہوتا ہے۔ یہ فراہم کردہ قدر کو ویلیوز ویکٹر میں شامل کرتا ہے۔

NEWCoroutine struct میں std::vector ممبر متغیر شامل ہوتا ہے جسے 'values' کہا جاتا ہے جو پیدا شدہ اقدار کی نمائندگی کرتا ہے۔ NEWCoroutine کے اندر، ایک nested struct iterator ہے جو پیدا شدہ اقدار پر اعادہ کرنے کی اجازت دیتا ہے۔ اس میں ایک coro_handle ہے جو coroutine کا ہینڈل ہے اور آپریٹرز کی وضاحت کرتا ہے جیسے !=، ++، اور * تکرار کے لیے۔

ہم coro_handle p_type وعدے سے حاصل کر کے کورٹین کے آغاز میں ایک تکرار کرنے والا بنانے کے لیے begin() فنکشن کا استعمال کرتے ہیں۔ جبکہ end() فنکشن ایک ایٹریٹر بناتا ہے جو کورٹین کے اختتام کی نمائندگی کرتا ہے اور اسے nullptr coro_handle کے ساتھ بنایا جاتا ہے۔ اس کے بعد وعدہ() فنکشن p_type وعدے سے ایک coroutine_handle بنا کر وعدے کی قسم واپس کرنے کے لیے استعمال کیا جاتا ہے۔ generateNumbers() فنکشن ایک کوروٹین ہے جو co_yield کلیدی لفظ کا استعمال کرتے ہوئے تین اقدار - 5، 6، اور 7 - حاصل کرتا ہے۔

مین () فنکشن میں، NEWCoroutine کی ایک مثال جس کا نام 'nc' ہے، generateNumbers() coroutine کو استعمال کرکے تخلیق کیا جاتا ہے۔ یہ کورٹین کو شروع کرتا ہے اور اس کی حالت پر قبضہ کرتا ہے۔ ایک رینج پر مبنی 'for' لوپ کا استعمال 'nc' کی قدروں پر اعادہ کرنے کے لیے کیا جاتا ہے، اور ہر ایک ویلیو پرنٹ کی جاتی ہے جو std::cout کا استعمال کرتے ہوئے اسپیس سے الگ ہوتی ہے۔

پیدا شدہ آؤٹ پٹ مندرجہ ذیل ہے:

نتیجہ

یہ مضمون C++ میں کوروٹینز کے استعمال کو ظاہر کرتا ہے۔ ہم نے دو مثالوں پر بات کی۔ پہلی مثال کے لیے، بنیادی کورٹین کو C++ پروگرام میں کورٹین فنکشنز کا استعمال کرتے ہوئے بنایا گیا ہے۔ جبکہ دوسرا مظاہرہ پیرامیٹرز کے ساتھ کورٹینز کو استعمال کرتے ہوئے اور نمبروں کی ترتیب بنانے کے لیے جنریٹر جیسا رویہ پیدا کرنے کے ذریعے کیا گیا۔