.NET: ملٹی تھریڈنگ اور اسینکرونی کے ساتھ کام کرنے کے لیے ٹولز۔ حصہ 1

میں اصل مضمون حبر پر شائع کر رہا ہوں، جس کا ترجمہ کارپوریٹ میں پوسٹ کیا گیا ہے۔ بلاگ پوسٹ.

یہاں اور اب نتیجہ کا انتظار کیے بغیر، غیر متوازی طور پر کچھ کرنے کی ضرورت، یا بڑے کام کو کئی اکائیوں میں تقسیم کرنے کی ضرورت، کمپیوٹر کی آمد سے پہلے موجود تھی۔ ان کی آمد سے یہ ضرورت بہت ٹھوس ہو گئی۔ اب، 2019 میں، میں یہ مضمون ایک لیپ ٹاپ پر 8 کور Intel Core پروسیسر کے ساتھ ٹائپ کر رہا ہوں، جس پر ایک سو سے زیادہ پروسیس متوازی چل رہے ہیں، اور اس سے بھی زیادہ تھریڈز۔ اس کے قریب ہی ایک قدرے گھٹیا فون ہے، جو چند سال پہلے خریدا گیا تھا، اس میں 8 کور پروسیسر ہے۔ موضوعاتی وسائل مضامین اور ویڈیوز سے بھرے ہوئے ہیں جہاں ان کے مصنفین اس سال کے فلیگ شپ اسمارٹ فونز کی تعریف کرتے ہیں جن میں 16 کور پروسیسر موجود ہیں۔ MS Azure ایک ورچوئل مشین فراہم کرتا ہے جس میں 20 کور پروسیسر اور 128 TB RAM $2/گھنٹہ سے کم ہے۔ بدقسمتی سے، دھاگوں کے تعامل کا انتظام کیے بغیر اس طاقت کو زیادہ سے زیادہ نکالنا اور استعمال کرنا ناممکن ہے۔

اصطلاحات

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

استعارہ۔

تمام تعریفیں اچھی نہیں ہیں اور کچھ کو اضافی وضاحت کی ضرورت ہے، اس لیے میں رسمی طور پر متعارف کرائی گئی اصطلاحات میں ناشتہ پکانے کے بارے میں ایک استعارہ شامل کروں گا۔ اس استعارے میں ناشتہ پکانا ایک عمل ہے۔

صبح ناشتے کی تیاری کے دوران میں (CPUمیں کچن میں آتا ہوں (کمپیوٹر)۔ میرے 2 ہاتھ ہیں (cores کی)۔ باورچی خانے میں بہت سے آلات ہیں (IO): تندور، کیتلی، ٹوسٹر، ریفریجریٹر۔ میں گیس آن کرتا ہوں، اس پر کڑاہی ڈالتا ہوں اور اس کے گرم ہونے کا انتظار کیے بغیر اس میں تیل ڈالتا ہوں (متضاد طور پر، نان بلاکنگ-IO-Wait)، میں انڈوں کو فریج سے نکال کر پلیٹ میں توڑ دیتا ہوں، پھر انہیں ایک ہاتھ سے مارتا ہوں (تھریڈ نمبر 1)، اور دوسرا (تھریڈ نمبر 2) پلیٹ پکڑنا (مشترکہ وسائل)۔ اب میں کیتلی آن کرنا چاہوں گا، لیکن میرے پاس اتنے ہاتھ نہیں ہیں (تھریڈ فاقہ) اس وقت کے دوران، فرائنگ پین گرم ہو جاتا ہے (نتیجے پر عملدرآمد) جس میں میں نے جو کوڑے مارے ہیں ڈالتا ہوں۔ میں کیتلی کے پاس پہنچتا ہوں اور اسے آن کرتا ہوں اور حماقت سے اس میں پانی کو ابلتا دیکھتا ہوں (بلاک کرنا-IO-Wait)، حالانکہ اس دوران وہ پلیٹ کو دھو سکتا تھا جہاں اس نے آملیٹ کو کوڑا تھا۔

میں نے صرف 2 ہاتھوں سے آملیٹ پکایا، اور میرے پاس زیادہ نہیں ہے، لیکن ایک ہی وقت میں، آملیٹ کو کوڑے مارنے کے وقت، ایک ساتھ 3 آپریشن ہوئے: آملیٹ کو کوڑے مارنا، پلیٹ پکڑنا، کڑاہی کو گرم کرنا۔ سی پی یو کمپیوٹر کا تیز ترین حصہ ہے، IO وہ ہے جو اکثر سب کچھ سست ہو جاتا ہے، اس لیے اکثر ایک موثر حل یہ ہے کہ IO سے ڈیٹا حاصل کرتے وقت CPU کو کسی چیز کے ساتھ رکھیں۔

استعارہ جاری رکھنا:

  • اگر آملیٹ بنانے کے دوران میں کپڑے بدلنے کی بھی کوشش کروں تو یہ ملٹی ٹاسکنگ کی ایک مثال ہوگی۔ ایک اہم نکتہ: کمپیوٹر اس میں لوگوں کے مقابلے میں بہت بہتر ہیں۔
  • کئی باورچیوں کے ساتھ ایک باورچی خانہ، مثال کے طور پر ایک ریستوراں میں - ایک ملٹی کور کمپیوٹر۔
  • ایک شاپنگ سینٹر میں فوڈ کورٹ میں بہت سے ریستوراں - ڈیٹا سینٹر

.NET ٹولز

.NET بہت سی دوسری چیزوں کی طرح تھریڈز کے ساتھ کام کرنے میں اچھا ہے۔ ہر نئے ورژن کے ساتھ، یہ ان کے ساتھ کام کرنے کے لیے زیادہ سے زیادہ نئے ٹولز متعارف کراتا ہے، OS تھریڈز پر تجرید کی نئی پرتیں۔ تجرید کی تعمیر کے ساتھ کام کرتے وقت، فریم ورک کے ڈویلپرز ایک ایسا نقطہ نظر استعمال کرتے ہیں جو موقع کو چھوڑ دیتا ہے، جب ایک اعلیٰ سطحی تجرید کا استعمال کرتے ہوئے، ایک یا زیادہ درجے نیچے جانے کے لیے۔ اکثر یہ ضروری نہیں ہوتا ہے، درحقیقت یہ شاٹ گن سے اپنے آپ کو پاؤں میں گولی مارنے کا دروازہ کھول دیتا ہے، لیکن بعض اوقات، شاذ و نادر صورتوں میں، یہ کسی ایسے مسئلے کو حل کرنے کا واحد طریقہ ہو سکتا ہے جو تجرید کی موجودہ سطح پر حل نہیں ہوتا۔ .

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

تھریڈ شروع کر رہا ہوں۔

تھریڈ کلاس .NET میں تھریڈز کے ساتھ کام کرنے کے لیے سب سے بنیادی کلاس ہے۔ کنسٹرکٹر دو مندوبین میں سے ایک کو قبول کرتا ہے:

  • ThreadStart - کوئی پیرامیٹرز نہیں۔
  • ParametrizedThreadStart - قسم کے آبجیکٹ کے ایک پیرامیٹر کے ساتھ۔

ڈیلیگیٹ کو اسٹارٹ میتھڈ کو کال کرنے کے بعد نئے بنائے گئے تھریڈ میں عمل میں لایا جائے گا۔ اگر ParametrizedThreadStart قسم کا ڈیلیگیٹ کنسٹرکٹر کو دیا گیا تھا، تو ایک آبجیکٹ کو اسٹارٹ میتھڈ میں پاس کرنا ہوگا۔ کسی بھی مقامی معلومات کو ندی میں منتقل کرنے کے لیے اس طریقہ کار کی ضرورت ہے۔ یہ بات قابل غور ہے کہ تھریڈ بنانا ایک مہنگا آپریشن ہے، اور تھریڈ خود ایک بھاری چیز ہے، کم از کم اس لیے کہ یہ اسٹیک پر 1MB میموری مختص کرتا ہے اور OS API کے ساتھ تعامل کی ضرورت ہوتی ہے۔

new Thread(...).Start(...);

ThreadPool کلاس پول کے تصور کی نمائندگی کرتی ہے۔ .NET میں، تھریڈ پول انجینئرنگ کا ایک ٹکڑا ہے، اور مائیکروسافٹ کے ڈویلپرز نے اس بات کو یقینی بنانے کے لیے کافی کوشش کی ہے کہ یہ مختلف قسم کے منظرناموں میں بہترین طریقے سے کام کرے۔

عمومی تصور:

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

پول سے تھریڈ استعمال کرنے کے لیے، ایک QueueUserWorkItem طریقہ موجود ہے جو WaitCallback قسم کے مندوب کو قبول کرتا ہے، جس کے دستخط ParametrizedThreadStart جیسے ہی ہوتے ہیں، اور اس کو پاس کیا جانے والا پیرامیٹر وہی کام کرتا ہے۔

ThreadPool.QueueUserWorkItem(...);

کم معروف تھریڈ پول طریقہ RegisterWaitForSingleObject نان بلاکنگ IO آپریشنز کو منظم کرنے کے لیے استعمال کیا جاتا ہے۔ اس طریقہ پر پاس کردہ مندوب کو اس وقت بلایا جائے گا جب طریقہ کو پاس کیا گیا WaitHandle "ریلیز" ہو گا۔

ThreadPool.RegisterWaitForSingleObject(...)

.NET کے پاس تھریڈ ٹائمر ہے اور یہ WinForms/WPF ٹائمرز سے مختلف ہے کہ اس کے ہینڈلر کو پول سے لیے گئے تھریڈ پر بلایا جائے گا۔

System.Threading.Timer

پول سے کسی دھاگے پر عمل درآمد کے لیے مندوب بھیجنے کا ایک غیر معمولی طریقہ بھی ہے - BeginInvoke طریقہ۔

DelegateInstance.BeginInvoke

میں مختصراً اس فنکشن پر غور کرنا چاہوں گا جس کے لیے اوپر کے بہت سے طریقوں کو کہا جا سکتا ہے - CreateThread from Kernel32.dll Win32 API۔ اس فنکشن کو کال کرنے کا ایک طریقہ ہے، بیرونی طریقوں کے طریقہ کار کی بدولت۔ میں نے اس طرح کی کال صرف ایک بار میراثی کوڈ کی ایک خوفناک مثال میں دیکھی ہے، اور مصنف کی حوصلہ افزائی جس نے بالکل ایسا کیا وہ اب بھی میرے لیے ایک معمہ بنی ہوئی ہے۔

Kernel32.dll CreateThread

دھاگوں کو دیکھنا اور ڈیبگ کرنا

آپ کے بنائے ہوئے تھریڈز، تمام تھرڈ پارٹی اجزاء، اور .NET پول کو Visual Studio کی Threads ونڈو میں دیکھا جا سکتا ہے۔ یہ ونڈو صرف اس وقت تھریڈ کی معلومات ظاہر کرے گی جب ایپلیکیشن ڈیبگ کے تحت ہو اور بریک موڈ میں ہو۔ یہاں آپ ہر تھریڈ کے اسٹیک کے نام اور ترجیحات کو آسانی سے دیکھ سکتے ہیں، اور ڈیبگنگ کو ایک مخصوص تھریڈ میں تبدیل کر سکتے ہیں۔ تھریڈ کلاس کی ترجیحی خاصیت کا استعمال کرتے ہوئے، آپ تھریڈ کی ترجیح سیٹ کر سکتے ہیں، جسے OC اور CLR دھاگوں کے درمیان پروسیسر کے وقت کو تقسیم کرتے وقت ایک سفارش کے طور پر سمجھیں گے۔

.NET: ملٹی تھریڈنگ اور اسینکرونی کے ساتھ کام کرنے کے لیے ٹولز۔ حصہ 1

ٹاسک متوازی لائبریری

ٹاسک پیریلل لائبریری (TPL) کو .NET 4.0 میں متعارف کرایا گیا تھا۔ اب یہ asynchrony کے ساتھ کام کرنے کا معیاری اور اہم ٹول ہے۔ کوئی بھی کوڈ جو پرانا طریقہ استعمال کرتا ہے اسے میراث سمجھا جاتا ہے۔ TPL کی بنیادی اکائی System.Threading.Tasks نام کی جگہ سے ٹاسک کلاس ہے۔ ایک کام ایک دھاگے پر ایک خلاصہ ہے۔ C# زبان کے نئے ورژن کے ساتھ، ہمیں Tasks - async/await operators کے ساتھ کام کرنے کا ایک خوبصورت طریقہ ملا۔ ان تصورات نے غیر مطابقت پذیر کوڈ لکھنا ممکن بنایا گویا یہ سادہ اور ہم وقت ساز تھا، اس سے تھریڈز کے اندرونی کام کے بارے میں بہت کم سمجھ رکھنے والے لوگوں کے لیے بھی ایسی ایپلی کیشنز لکھنا ممکن ہوا جو ان کا استعمال کرتی ہیں، ایسی ایپلی کیشنز جو طویل آپریشنز کرتے وقت منجمد نہیں ہوتیں۔ async/await کا استعمال ایک یا کئی مضامین کے لیے ایک موضوع ہے، لیکن میں اس کا خلاصہ چند جملوں میں حاصل کرنے کی کوشش کروں گا:

  • async ٹاسک یا باطل کو واپس کرنے والے طریقہ کا ایک ترمیم کنندہ ہے۔
  • اور await ایک نان بلاکنگ ٹاسک ویٹنگ آپریٹر ہے۔

ایک بار پھر: await آپریٹر، عام صورت میں (مستثنیات ہیں)، عمل درآمد کے موجودہ تھریڈ کو مزید جاری کرے گا، اور جب ٹاسک اپنا عمل مکمل کر لے گا، اور تھریڈ (حقیقت میں، سیاق و سباق کہنا زیادہ درست ہوگا۔ ، لیکن بعد میں اس پر مزید) طریقہ کار پر مزید عمل درآمد جاری رکھیں گے۔ .NET کے اندر، یہ میکانزم اسی طرح لاگو ہوتا ہے جس طرح پیداوار کی واپسی، جب تحریری طریقہ ایک پوری کلاس میں بدل جاتا ہے، جو ایک ریاستی مشین ہے اور ان ریاستوں کے لحاظ سے الگ الگ ٹکڑوں میں عمل میں لایا جا سکتا ہے۔ کوئی بھی دلچسپی رکھنے والا کوئی بھی سادہ کوڈ asynс/await کا استعمال کرتے ہوئے لکھ سکتا ہے، کمپائلر جنریٹڈ کوڈ کے ساتھ JetBrains dotPeek کا استعمال کرتے ہوئے اسمبلی کو مرتب اور دیکھ سکتا ہے۔

آئیے ٹاسک کو لانچ کرنے اور استعمال کرنے کے آپشنز کو دیکھتے ہیں۔ ذیل میں کوڈ کی مثال میں، ہم ایک نیا کام بناتے ہیں جو کچھ بھی کارآمد نہیں ہوتا ہے (Thread.Sleep(10000))، لیکن حقیقی زندگی میں یہ کچھ پیچیدہ CPU-انتہائی کام ہونا چاہیے۔

using TCO = System.Threading.Tasks.TaskCreationOptions;

public static async void VoidAsyncMethod() {
    var cancellationSource = new CancellationTokenSource();

    await Task.Factory.StartNew(
        // Code of action will be executed on other context
        () => Thread.Sleep(10000),
        cancellationSource.Token,
        TCO.LongRunning | TCO.AttachedToParent | TCO.PreferFairness,
        scheduler
    );

    //  Code after await will be executed on captured context
}

ایک ٹاسک کئی اختیارات کے ساتھ بنایا گیا ہے:

  • لانگ رننگ اس بات کا اشارہ ہے کہ ٹاسک تیزی سے مکمل نہیں ہوگا، جس کا مطلب ہے کہ پول سے دھاگہ نہ لینے پر غور کیا جائے، بلکہ اس ٹاسک کے لیے الگ سے دھاگہ بنانا چاہیے تاکہ دوسروں کو نقصان نہ پہنچے۔
  • AttachedToParent - کاموں کو درجہ بندی میں ترتیب دیا جا سکتا ہے۔ اگر یہ آپشن استعمال کیا گیا تھا، تو ٹاسک اس حالت میں ہو سکتا ہے جہاں اس نے خود مکمل کر لیا ہو اور اپنے بچوں کی سزا کا انتظار کر رہا ہو۔
  • PreferFairness - کا مطلب ہے کہ بعد میں بھیجے گئے کاموں سے پہلے عمل درآمد کے لیے بھیجے گئے کاموں کو انجام دینا بہتر ہوگا۔ لیکن یہ صرف ایک سفارش ہے اور نتائج کی ضمانت نہیں ہے۔

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

آخری پیرامیٹر TaskScheduler قسم کا ایک شیڈیولر آبجیکٹ ہے۔ یہ کلاس اور اس کی اولاد کو ٹاسک کو تھریڈز میں تقسیم کرنے کی حکمت عملیوں کو کنٹرول کرنے کے لیے ڈیزائن کیا گیا ہے؛ بطور ڈیفالٹ، ٹاسک کو پول سے بے ترتیب دھاگے پر عمل میں لایا جائے گا۔

انتظار کرنے والے آپریٹر کو تخلیق کردہ ٹاسک پر لاگو کیا جاتا ہے، جس کا مطلب ہے کہ اس کے بعد لکھا ہوا کوڈ، اگر کوئی ہے تو، اسی تناظر میں عمل میں لایا جائے گا (اکثر اس کا مطلب اسی دھاگے پر ہے) جیسا کہ انتظار سے پہلے کوڈ تھا۔

طریقہ کو async void کے طور پر نشان زد کیا گیا ہے، جس کا مطلب ہے کہ یہ await آپریٹر استعمال کر سکتا ہے، لیکن کالنگ کوڈ عمل درآمد کا انتظار نہیں کر سکے گا۔ اگر ایسی خصوصیت ضروری ہے، تو طریقہ کار کو ٹاسک واپس کرنا ہوگا۔ async void کے نشان والے طریقے کافی عام ہیں: ایک اصول کے طور پر، یہ ایونٹ ہینڈلر یا دوسرے طریقے ہیں جو آگ پر کام کرتے ہیں اور اصول کو بھول جاتے ہیں۔ اگر آپ کو نہ صرف پھانسی کے اختتام تک انتظار کرنے کا موقع دینا ہے، بلکہ نتیجہ بھی واپس کرنا ہے، تو آپ کو ٹاسک استعمال کرنے کی ضرورت ہے۔

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

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

public static async void AnotherMethod() {

    int result = await AsyncMethod(); // good

    result = AsyncMethod().Result; // bad

    AsyncMethod().Wait(); // bad

    IEnumerable<Task> tasks = new Task[] {
        AsyncMethod(), OtherAsyncMethod()
    };

    await Task.WhenAll(tasks); // good
    await Task.WhenAny(tasks); // good

    Task.WaitAll(tasks.ToArray()); // bad
}

پہلی مثال میں، ہم کالنگ تھریڈ کو بلاک کیے بغیر ٹاسک کے مکمل ہونے کا انتظار کرتے ہیں؛ ہم نتیجہ کی پروسیسنگ میں صرف اس وقت واپس آئیں گے جب یہ پہلے سے موجود ہو؛ تب تک، کالنگ تھریڈ کو اس کے اپنے آلات پر چھوڑ دیا جاتا ہے۔

دوسرے آپشن میں، ہم کالنگ تھریڈ کو اس وقت تک بلاک کر دیتے ہیں جب تک کہ طریقہ کار کا نتیجہ نہیں نکل جاتا۔ یہ نہ صرف اس لیے برا ہے کہ ہم نے پروگرام کے اتنے قیمتی وسیلہ پر، سادہ سستی کے ساتھ قبضہ کر لیا ہے، بلکہ اس لیے بھی کہ اگر ہم جس طریقہ کار کو کال کرتے ہیں اس کا کوڈ منتظر ہے، اور مطابقت پذیری کے سیاق و سباق کے بعد کالنگ تھریڈ پر واپس جانے کی ضرورت ہے۔ انتظار کریں، پھر ہمیں ایک تعطل ملے گا : کالنگ تھریڈ انتظار کرتا ہے جب تک کہ غیر مطابقت پذیر طریقہ کا نتیجہ شمار کیا جاتا ہے، غیر مطابقت پذیر طریقہ کالنگ تھریڈ میں اپنا عمل جاری رکھنے کی بیکار کوشش کرتا ہے۔

اس نقطہ نظر کا ایک اور نقصان پیچیدہ غلطی کو سنبھالنا ہے۔ حقیقت یہ ہے کہ async/await استعمال کرتے وقت غیر مطابقت پذیر کوڈ میں غلطیاں ہینڈل کرنے میں بہت آسان ہیں - وہ اسی طرح برتاؤ کرتی ہیں جیسے کوڈ مطابقت پذیر ہو۔ جب کہ اگر ہم کسی ٹاسک پر ہم وقتی انتظار کے اخراج کو لاگو کرتے ہیں، تو اصل استثنا AggregateException میں بدل جاتا ہے، یعنی استثنیٰ کو ہینڈل کرنے کے لیے، آپ کو InnerException قسم کی جانچ کرنی ہوگی اور ایک کیچ بلاک کے اندر if chain آپ کو لکھنا ہوگا یا C# دنیا میں زیادہ مانوس کیچ بلاکس کی زنجیر کے بجائے کیچ کو تعمیر کرتے وقت استعمال کرنا ہوگا۔

تیسری اور آخری مثالوں کو بھی اسی وجہ سے خراب قرار دیا گیا ہے اور ان میں تمام مسائل ایک جیسے ہیں۔

WhenAny اور WhenAll طریقے کاموں کے ایک گروپ کے انتظار کے لیے انتہائی آسان ہیں؛ وہ ٹاسک کے ایک گروپ کو ایک میں لپیٹ دیتے ہیں، جو یا تو اس وقت فائر ہو جائیں گے جب گروپ سے کوئی ٹاسک پہلی بار شروع ہو جائے، یا جب ان سب نے اپنا عمل مکمل کر لیا ہو۔

دھاگوں کو روکنا

مختلف وجوہات کی بناء پر، بہاؤ شروع ہونے کے بعد اسے روکنا ضروری ہو سکتا ہے۔ ایسا کرنے کے کئی طریقے ہیں۔ تھریڈ کلاس میں دو مناسب طریقے سے نامزد طریقے ہیں: منسوخ کریں и مداخلت کریں. سب سے پہلے استعمال کے لئے انتہائی سفارش نہیں کی جاتی ہے، کیونکہ کسی بھی تصادفی لمحے پر کال کرنے کے بعد، کسی بھی ہدایت کی پروسیسنگ کے دوران، ایک استثناء دیا جائے گا ThreadAbortedException. آپ کسی بھی انٹیجر متغیر کو بڑھاتے وقت ایسی رعایت کی توقع نہیں کرتے، ٹھیک ہے؟ اور یہ طریقہ استعمال کرتے وقت، یہ ایک بہت ہی حقیقی صورتحال ہے۔ اگر آپ کو کوڈ کے مخصوص حصے میں CLR کو ایسی استثنا پیدا کرنے سے روکنے کی ضرورت ہے، تو آپ اسے کالز میں لپیٹ سکتے ہیں۔ Thread.BeginCriticalRegion, Thread.EndCriticalRegion. آخر میں بلاک میں لکھا ہوا کوئی بھی کوڈ ایسی کالوں میں لپیٹ دیا جاتا ہے۔ اس وجہ سے، فریم ورک کوڈ کی گہرائی میں آپ کو خالی کوشش کے ساتھ بلاکس مل سکتے ہیں، لیکن آخر میں خالی نہیں۔ مائیکروسافٹ اس طریقہ کار کی اس قدر حوصلہ شکنی کرتا ہے کہ اس نے اسے .net کور میں شامل نہیں کیا۔

مداخلت کا طریقہ زیادہ متوقع طور پر کام کرتا ہے۔ یہ ایک استثناء کے ساتھ تھریڈ میں خلل ڈال سکتا ہے۔ ThreadInterruptedException صرف ان لمحات کے دوران جب تھریڈ انتظار کی حالت میں ہو۔ یہ WaitHandle، لاک، یا Thread.Sleep کو کال کرنے کے بعد لٹکتے ہوئے اس حالت میں داخل ہوتا ہے۔

اوپر بیان کردہ دونوں اختیارات ان کی غیر متوقع ہونے کی وجہ سے خراب ہیں۔ اس کا حل ایک ڈھانچہ استعمال کرنا ہے۔ منسوخی ٹوکن اور کلاس کینسلیشن ٹوکن سورس. نکتہ یہ ہے: CancellationTokenSource کلاس کی ایک مثال بنائی گئی ہے اور صرف وہی جو اس کا مالک ہے اس طریقہ کار کو کال کر کے آپریشن کو روک سکتا ہے۔ منسوخ کریں. صرف کینسلیشن ٹوکن ہی آپریشن کو دیا جاتا ہے۔ CancellationToken کے مالکان خود آپریشن کو منسوخ نہیں کر سکتے، لیکن صرف یہ چیک کر سکتے ہیں کہ آیا آپریشن منسوخ ہو گیا ہے۔ اس کے لیے بولین پراپرٹی ہے۔ منسوخی کی درخواست کی گئی ہے۔ اور طریقہ ThrowIfCancelRequested. مؤخر الذکر ایک استثناء پھینک دے گا۔ TaskCancelledException اگر CancelationToken مثال کے طور پر طوطے کیے جانے پر کینسل طریقہ کو بلایا گیا تھا۔ اور یہ وہ طریقہ ہے جسے میں استعمال کرنے کی تجویز کرتا ہوں۔ یہ پچھلے اختیارات کے مقابلے میں ایک بہتری ہے جس پر مکمل کنٹرول حاصل کر کے کسی استثنائی آپریشن کو کس مقام پر ختم کیا جا سکتا ہے۔

تھریڈ کو روکنے کا سب سے ظالمانہ آپشن Win32 API TerminateThread فنکشن کو کال کرنا ہے۔ اس فنکشن کو کال کرنے کے بعد CLR کا رویہ غیر متوقع ہو سکتا ہے۔ MSDN پر اس فنکشن کے بارے میں درج ذیل لکھا گیا ہے۔ "ٹرمینیٹ تھریڈ ایک خطرناک فنکشن ہے جو صرف انتہائی انتہائی صورتوں میں استعمال کیا جانا چاہئے۔ "

FromAsync طریقہ استعمال کرتے ہوئے Legacy API کو Task Based میں تبدیل کرنا

اگر آپ کسی ایسے پروجیکٹ پر کام کرنے کے لیے کافی خوش قسمت ہیں جو ٹاسکس متعارف کرائے جانے کے بعد شروع کیا گیا تھا اور زیادہ تر ڈویلپرز کے لیے خاموشی سے خوف پیدا کرنا بند کر دیا تھا، تو آپ کو بہت سارے پرانے APIs سے نمٹنا نہیں پڑے گا، فریق ثالث اور آپ کی ٹیم دونوں۔ ماضی میں تشدد کیا ہے. خوش قسمتی سے، .NET Framework ٹیم نے ہماری دیکھ بھال کی، حالانکہ شاید مقصد اپنا خیال رکھنا تھا۔ جیسا کہ ہوسکتا ہے، .NET کے پاس پرانے غیر مطابقت پذیر پروگرامنگ اپروچز میں لکھے گئے کوڈ کو بغیر کسی تکلیف کے تبدیل کرنے کے لیے بہت سے ٹولز ہیں۔ ان میں سے ایک ٹاسک فیکٹری کا FromAsync طریقہ ہے۔ ذیل میں کوڈ کی مثال میں، میں اس طریقہ کو استعمال کرتے ہوئے WebRequest کلاس کے پرانے async طریقوں کو ٹاسک میں لپیٹتا ہوں۔

object state = null;
WebRequest wr = WebRequest.CreateHttp("http://github.com");
await Task.Factory.FromAsync(
    wr.BeginGetResponse,
    we.EndGetResponse
);

یہ صرف ایک مثال ہے اور آپ کو بلٹ ان اقسام کے ساتھ ایسا کرنے کا امکان نہیں ہے، لیکن کوئی بھی پرانا پروجیکٹ صرف BeginDoSomething طریقوں سے مل رہا ہے جو IAsyncResult اور EndDoSomething طریقوں کو واپس کرتے ہیں جو اسے وصول کرتے ہیں۔

TaskCompletionSource کلاس کا استعمال کرتے ہوئے Legacy API کو Task Based میں تبدیل کریں۔

غور کرنے کا ایک اور اہم ٹول کلاس ہے۔ Task Completion Source. افعال، مقصد اور آپریشن کے اصول کے لحاظ سے، یہ ThreadPool کلاس کے RegisterWaitForSingleObject طریقہ کی کسی حد تک یاد دلاتا ہے، جس کے بارے میں میں نے اوپر لکھا ہے۔ اس کلاس کا استعمال کرتے ہوئے، آپ ٹاسکس میں پرانے غیر مطابقت پذیر APIs کو آسانی سے اور آسانی سے لپیٹ سکتے ہیں۔

آپ کہیں گے کہ میں پہلے ہی ان مقاصد کے لیے بنائے گئے TaskFactory کلاس کے FromAsync طریقہ کے بارے میں بات کر چکا ہوں۔ یہاں ہمیں .net میں غیر مطابقت پذیر ماڈلز کی ترقی کی پوری تاریخ کو یاد رکھنا ہوگا جو مائیکروسافٹ نے پچھلے 15 سالوں میں پیش کیا ہے: ٹاسک بیسڈ اسینکرونس پیٹرن (ٹی اے پی) سے پہلے، اسینکرونس پروگرامنگ پیٹرن (اے پی پی) موجود تھا، جو طریقوں کے بارے میں تھا شروعکچھ واپس آ رہا ہے۔ IAsyncResult اور طریقے اختتامDoSomething جو اسے قبول کرتا ہے اور ان سالوں کی وراثت کے لیے FromAsync طریقہ بالکل کامل ہے، لیکن وقت گزرنے کے ساتھ، اس کی جگہ ایونٹ بیسڈ غیر مطابقت پذیر پیٹرن (EAP)، جس نے فرض کیا کہ غیر مطابقت پذیر آپریشن مکمل ہونے پر ایک واقعہ اٹھایا جائے گا۔

TaskCompletionSource ایونٹ ماڈل کے ارد گرد بنائے گئے Tasks اور Legacy APIs کو سمیٹنے کے لیے بہترین ہے۔ اس کے کام کا نچوڑ کچھ یوں ہے: اس کلاس کے کسی شے کے پاس ٹاسک قسم کی پبلک پراپرٹی ہوتی ہے، جس کی حالت کو SetResult، SetException وغیرہ TaskCompletionSource کلاس کے طریقوں سے کنٹرول کیا جا سکتا ہے۔ ان جگہوں پر جہاں انتظار آپریٹر کو اس ٹاسک پر لاگو کیا گیا تھا، یہ ٹاسک کامپلیشن سورس پر لاگو کیے گئے طریقہ کے لحاظ سے ایک استثناء کے ساتھ مکمل یا ناکام ہو جائے گا۔ اگر یہ ابھی تک واضح نہیں ہے، تو آئیے اس کوڈ کی مثال کو دیکھیں، جہاں کچھ پرانے EAP API کو TaskCompletionSource کا استعمال کرتے ہوئے ٹاسک میں لپیٹ دیا گیا ہے: جب ایونٹ فائر ہوتا ہے، تو ٹاسک کو مکمل حالت میں رکھا جائے گا، اور وہ طریقہ جس نے انتظار کرنے والے آپریٹر کو لاگو کیا ہے۔ اس ٹاسک پر اعتراض موصول ہونے کے بعد اس پر عمل درآمد دوبارہ شروع ہو جائے گا۔ نتیجہ.

public static Task<Result> DoAsync(this SomeApiInstance someApiObj) {

    var completionSource = new TaskCompletionSource<Result>();
    someApiObj.Done += 
        result => completionSource.SetResult(result);
    someApiObj.Do();

    result completionSource.Task;
}

Task CompletionSource Tips & Tricks

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

مختصراً، چال کا نچوڑ یہ ہے: آپ کو API سے اس کے اطراف میں ہونے والے کچھ واقعات کے بارے میں معلومات حاصل کرنے کی ضرورت ہے، جبکہ API کسی وجہ سے واقعہ کی اطلاع نہیں دے سکتا، لیکن صرف ریاست کو واپس کر سکتا ہے۔ ان کی ایک مثال WebSocket کے وقت سے پہلے HTTP کے اوپر بنائے گئے تمام APIs ہیں یا جب کسی وجہ سے اس ٹیکنالوجی کو استعمال کرنا ناممکن تھا۔ کلائنٹ HTTP سرور سے پوچھ سکتا ہے۔ HTTP سرور خود کلائنٹ کے ساتھ مواصلت شروع نہیں کر سکتا۔ ایک آسان حل یہ ہے کہ ٹائمر کا استعمال کرتے ہوئے سرور کو پول کیا جائے، لیکن اس سے سرور پر اضافی بوجھ پیدا ہوتا ہے اور اوسط TimerInterval / 2 میں اضافی تاخیر ہوتی ہے۔ اس کو حاصل کرنے کے لیے لانگ پولنگ نامی ایک چال ایجاد کی گئی، جس میں جواب میں تاخیر شامل ہے۔ سرور جب تک ٹائم آؤٹ ختم نہیں ہوتا یا کوئی واقعہ پیش نہیں آتا۔ اگر کوئی واقعہ پیش آتا ہے، تو اس پر کارروائی کی جاتی ہے؛ اگر نہیں، تو درخواست دوبارہ بھیجی جاتی ہے۔

while(!eventOccures && !timeoutExceeded)  {

  CheckTimout();
  CheckEvent();
  Thread.Sleep(1);
}

لیکن اس طرح کا حل خوفناک ثابت ہوگا جیسے ہی ایونٹ کا انتظار کرنے والے کلائنٹس کی تعداد بڑھے گی، کیونکہ... ہر اس طرح کا کلائنٹ ایک ایونٹ کے انتظار میں ایک پورے تھریڈ پر قبضہ کرتا ہے۔ ہاں، اور ایونٹ کے متحرک ہونے پر ہمیں 1ms کی اضافی تاخیر ملتی ہے، اکثر یہ اہم نہیں ہوتا، لیکن سافٹ ویئر کو اس سے زیادہ خراب کیوں بنایا جاتا ہے؟ اگر ہم Thread.Sleep(1) کو ہٹاتے ہیں، تو بیکار میں ہم ایک پروسیسر کور 100% بیکار لوڈ کر دیں گے، ایک بیکار چکر میں گھوم رہے ہیں۔ TaskCompletionSource کا استعمال کرتے ہوئے آپ آسانی سے اس کوڈ کو دوبارہ بنا سکتے ہیں اور مذکورہ بالا تمام مسائل کو حل کر سکتے ہیں:

class LongPollingApi {

    private Dictionary<int, TaskCompletionSource<Msg>> tasks;

    public async Task<Msg> AcceptMessageAsync(int userId, int duration) {

        var cs = new TaskCompletionSource<Msg>();
        tasks[userId] = cs;
        await Task.WhenAny(Task.Delay(duration), cs.Task);
        return cs.Task.IsCompleted ? cs.Task.Result : null;
    }

    public void SendMessage(int userId, Msg m) {

        if (tasks.TryGetValue(userId, out var completionSource))
            completionSource.SetResult(m);
    }
}

یہ کوڈ پروڈکشن کے لیے تیار نہیں ہے، بلکہ صرف ایک ڈیمو ہے۔ اسے حقیقی صورتوں میں استعمال کرنے کے لیے، آپ کو کم از کم صورت حال کو سنبھالنے کی ضرورت ہے جب کوئی پیغام ایسے وقت میں آتا ہے جب کوئی اس کی توقع نہیں کر رہا ہوتا ہے: اس صورت میں، AsseptMessageAsync طریقہ کو پہلے سے مکمل شدہ ٹاسک واپس کرنا چاہیے۔ اگر یہ سب سے عام معاملہ ہے، تو آپ ValueTask استعمال کرنے کے بارے میں سوچ سکتے ہیں۔

جب ہمیں کسی پیغام کی درخواست موصول ہوتی ہے، تو ہم ڈکشنری میں ایک TaskCompletionSource بناتے ہیں اور رکھتے ہیں، اور پھر انتظار کرتے ہیں کہ پہلے کیا ہوتا ہے: مخصوص وقت کا وقفہ ختم ہوتا ہے یا پیغام موصول ہوتا ہے۔

ویلیو ٹاسک: کیوں اور کیسے

async/await آپریٹرز، yield ریٹرن آپریٹر کی طرح، طریقہ سے ایک سٹیٹ مشین تیار کرتے ہیں، اور یہ ایک نئی آبجیکٹ کی تخلیق ہے، جو تقریباً ہمیشہ اہم نہیں ہوتی، لیکن شاذ و نادر صورتوں میں یہ ایک مسئلہ پیدا کر سکتی ہے۔ یہ معاملہ ایک ایسا طریقہ ہو سکتا ہے جسے واقعی اکثر کہا جاتا ہے، ہم دسیوں اور فی سیکنڈ ہزاروں کالوں کے بارے میں بات کر رہے ہیں۔ اگر ایسا طریقہ اس طرح لکھا جاتا ہے کہ زیادہ تر معاملات میں یہ انتظار کے تمام طریقوں کو نظرانداز کرتے ہوئے نتیجہ دیتا ہے، تو .NET اس کو بہتر بنانے کے لیے ایک ٹول فراہم کرتا ہے - ValueTask ڈھانچہ۔ اسے واضح کرنے کے لیے، آئیے اس کے استعمال کی ایک مثال دیکھیں: ایک ذخیرہ ہے جس پر ہم اکثر جاتے ہیں۔ اس میں کچھ قدریں ہیں اور پھر ہم انہیں آسانی سے واپس کر دیتے ہیں؛ اگر نہیں، تو ہم انہیں حاصل کرنے کے لیے کچھ سست IO پر جاتے ہیں۔ میں مؤخر الذکر کو متضاد طور پر کرنا چاہتا ہوں، جس کا مطلب ہے کہ پورا طریقہ غیر مطابقت پذیر نکلا۔ اس طرح، طریقہ لکھنے کا واضح طریقہ درج ذیل ہے:

public async Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return val;
    return await RequestById(id);
}

اس کوڈ کو مرتب کرتے وقت تھوڑا سا بہتر بنانے کی خواہش، اور اس بارے میں تھوڑا سا خوف کہ Roslyn کیا پیدا کرے گا، آپ اس مثال کو اس طرح دوبارہ لکھ سکتے ہیں:

public Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return Task.FromResult(val);
    return RequestById(id);
}

درحقیقت، اس معاملے میں بہترین حل یہ ہوگا کہ ہاٹ پاتھ کو بہتر بنایا جائے، یعنی بغیر کسی غیر ضروری مختص اور جی سی پر بوجھ کے لغت سے قدر حاصل کرنا، جبکہ ان شاذ و نادر صورتوں میں جب ہمیں ڈیٹا کے لیے IO پر جانے کی ضرورت ہوتی ہے۔ ، ہر چیز پرانے طریقے سے پلس/مائنس رہے گی:

public ValueTask<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return new ValueTask<string>(val);
    return new ValueTask<string>(RequestById(id));
}

آئیے کوڈ کے اس ٹکڑے کو قریب سے دیکھیں: اگر کیشے میں کوئی قدر ہے تو ہم ایک ڈھانچہ بناتے ہیں، ورنہ اصل کام ایک معنی خیز میں لپیٹ دیا جائے گا۔ کالنگ کوڈ کو اس بات کی کوئی پرواہ نہیں ہے کہ اس کوڈ کو کس راستے پر عمل میں لایا گیا ہے: ValueTask، C# نحوی نقطہ نظر سے، اس معاملے میں ایک عام ٹاسک کی طرح برتاؤ کرے گا۔

ٹاسک شیڈیولرز: ٹاسک لانچ کی حکمت عملیوں کا انتظام کرنا

اگلا API جس پر میں غور کرنا چاہتا ہوں وہ کلاس ہے۔ ٹاسک شیڈیولر اور اس کے مشتقات۔ میں نے پہلے ہی اوپر ذکر کیا ہے کہ TPL تمام تھریڈز میں کاموں کو تقسیم کرنے کے لیے حکمت عملیوں کو منظم کرنے کی صلاحیت رکھتا ہے۔ اس طرح کی حکمت عملیوں کی تعریف TaskScheduler کلاس کی اولاد میں کی گئی ہے۔ تقریباً کوئی بھی حکمت عملی جس کی آپ کو ضرورت ہو لائبریری میں مل سکتی ہے۔ متوازی توسیعات, Microsoft کی طرف سے تیار کیا گیا ہے، لیکن .NET کا حصہ نہیں ہے، لیکن ایک Nuget پیکیج کے طور پر فراہم کیا گیا ہے۔ آئیے ان میں سے کچھ کو مختصراً دیکھتے ہیں:

  • کرنٹ تھریڈ ٹاسک شیڈیولر - موجودہ تھریڈ پر کاموں کو انجام دیتا ہے۔
  • LimitedConcurrency LevelTaskScheduler - پیرامیٹر N کے ذریعہ بیک وقت انجام پانے والے کاموں کی تعداد کو محدود کرتا ہے، جسے کنسٹرکٹر میں قبول کیا جاتا ہے۔
  • OrderedTaskScheduler — کی تعریف LimitedConcurrencyLevelTaskScheduler(1) کے طور پر کی گئی ہے، لہذا کاموں کو ترتیب وار انجام دیا جائے گا۔
  • WorkStealingTaskScheduler - لاگو کرتا ہے کام چوری کام کی تقسیم کا نقطہ نظر بنیادی طور پر یہ ایک الگ تھریڈ پول ہے۔ اس مسئلے کو حل کرتا ہے کہ .NET ThreadPool میں ایک جامد کلاس ہے، جو تمام ایپلی کیشنز کے لیے ایک ہے، جس کا مطلب ہے کہ پروگرام کے ایک حصے میں اس کا زیادہ بوجھ یا غلط استعمال دوسرے حصے میں ضمنی اثرات کا باعث بن سکتا ہے۔ مزید یہ کہ اس طرح کے نقائص کی وجہ کو سمجھنا بہت مشکل ہے۔ وہ. پروگرام کے ان حصوں میں علیحدہ WorkStealingTaskSchedulers استعمال کرنے کی ضرورت ہو سکتی ہے جہاں ThreadPool کا استعمال جارحانہ اور غیر متوقع ہو سکتا ہے۔
  • QuueedTaskScheduler - آپ کو ترجیحی قطار کے قواعد کے مطابق کام انجام دینے کی اجازت دیتا ہے۔
  • ThreadPerTaskScheduler - ہر ٹاسک کے لیے الگ تھریڈ بناتا ہے جو اس پر عمل میں آتا ہے۔ ان کاموں کے لیے مفید ہو سکتا ہے جن کو مکمل ہونے میں غیر متوقع طور پر طویل وقت لگتا ہے۔

اچھی تفصیل ہے۔ مضمون مائیکروسافٹ بلاگ پر TaskSchedulers کے بارے میں۔

ٹاسکس سے متعلق ہر چیز کی آسان ڈیبگنگ کے لیے، ویژول اسٹوڈیو میں ٹاسکس ونڈو ہے۔ اس ونڈو میں آپ ٹاسک کی موجودہ حالت دیکھ سکتے ہیں اور کوڈ کی فی الحال عملدرآمد کرنے والی لائن پر جا سکتے ہیں۔

.NET: ملٹی تھریڈنگ اور اسینکرونی کے ساتھ کام کرنے کے لیے ٹولز۔ حصہ 1

PLinq اور متوازی کلاس

ٹاسکس اور ان کے بارے میں کہی گئی ہر چیز کے علاوہ، .NET میں دو اور دلچسپ ٹولز ہیں: PLinq (Linq2Parallel) اور Parallel class۔ پہلا متعدد دھاگوں پر لنک کے تمام آپریشنز کے متوازی عمل درآمد کا وعدہ کرتا ہے۔ دھاگوں کی تعداد کو WithDegreeOfParallelism ایکسٹینشن کا طریقہ استعمال کرتے ہوئے ترتیب دیا جا سکتا ہے۔ بدقسمتی سے، اکثر اوقات PLinq کے پاس اپنے ڈیفالٹ موڈ میں آپ کے ڈیٹا سورس کے انٹرنلز کے بارے میں اتنی معلومات نہیں ہوتی ہیں کہ وہ ایک اہم رفتار حاصل کر سکے، دوسری طرف، کوشش کرنے کی قیمت بہت کم ہے: آپ کو صرف AsParallel طریقہ کو کال کرنے کی ضرورت ہے۔ لنک کے طریقوں کا سلسلہ اور کارکردگی کے ٹیسٹ چلائیں۔ مزید یہ کہ پارٹیشنز میکانزم کا استعمال کرتے ہوئے آپ کے ڈیٹا سورس کی نوعیت کے بارے میں PLinq کو اضافی معلومات بھیجنا ممکن ہے۔ آپ مزید پڑھ سکتے ہیں۔ یہاں и یہاں.

متوازی جامد کلاس متوازی طور پر فاریچ کلیکشن کے ذریعے تکرار کرنے، فار لوپ کو عمل میں لانے، اور متوازی انووک میں متعدد مندوبین کو انجام دینے کے طریقے فراہم کرتی ہے۔ حسابات مکمل ہونے تک موجودہ تھریڈ پر عمل درآمد روک دیا جائے گا۔ آخری دلیل کے طور پر ParallelOptions کو پاس کر کے تھریڈز کی تعداد کو ترتیب دیا جا سکتا ہے۔ آپ اختیارات کا استعمال کرتے ہوئے TaskScheduler اور CancellationToken کی بھی وضاحت کر سکتے ہیں۔

نتائج

جب میں نے اپنی رپورٹ کے مواد اور اس کے بعد اپنے کام کے دوران جمع کی گئی معلومات کی بنیاد پر یہ مضمون لکھنا شروع کیا تو مجھے امید نہیں تھی کہ اس میں اتنا کچھ ہوگا۔ اب، جب ٹیکسٹ ایڈیٹر جس میں میں یہ مضمون ٹائپ کر رہا ہوں، مجھے ملامت کے ساتھ بتاتا ہے کہ صفحہ 15 چلا گیا ہے، میں عبوری نتائج کا خلاصہ کروں گا۔ دیگر چالوں، APIs، بصری آلات اور نقصانات کا احاطہ اگلے مضمون میں کیا جائے گا۔

نتیجہ:

  • جدید پی سی کے وسائل کو استعمال کرنے کے لیے آپ کو تھریڈز، غیر مطابقت پذیری اور ہم آہنگی کے ساتھ کام کرنے کے لیے ٹولز جاننے کی ضرورت ہے۔
  • .NET کے پاس ان مقاصد کے لیے بہت سے مختلف ٹولز ہیں۔
  • یہ سب ایک ساتھ ظاہر نہیں ہوئے، اس لیے آپ اکثر میراثی تلاش کر سکتے ہیں، تاہم، پرانے APIs کو بغیر کسی کوشش کے تبدیل کرنے کے طریقے موجود ہیں۔
  • .NET میں تھریڈز کے ساتھ کام کرنا Thread اور ThreadPool کلاسز کی نمائندگی کرتا ہے۔
  • Thread.Abort، Thread.Interrupt، اور Win32 API TerminateThread طریقے خطرناک ہیں اور استعمال کے لیے تجویز نہیں کیے جاتے۔ اس کے بجائے، CancellationToken میکانزم کا استعمال کرنا بہتر ہے۔
  • بہاؤ ایک قیمتی وسیلہ ہے اور اس کی فراہمی محدود ہے۔ ایسے حالات سے گریز کیا جائے جہاں دھاگے واقعات کے انتظار میں مصروف ہوں۔ اس کے لیے TaskCompletionSource کلاس استعمال کرنا آسان ہے۔
  • متوازی اور مطابقت پذیری کے ساتھ کام کرنے کے لیے سب سے طاقتور اور جدید ترین .NET ٹولز ٹاسکس ہیں۔
  • c# async/await آپریٹرز نان بلاکنگ انتظار کے تصور کو نافذ کرتے ہیں۔
  • آپ TaskScheduler سے ماخوذ کلاسز کا استعمال کرتے ہوئے تمام تھریڈز میں Tasks کی تقسیم کو کنٹرول کر سکتے ہیں
  • ویلیو ٹاسک کا ڈھانچہ ہاٹ پاتھ اور میموری ٹریفک کو بہتر بنانے میں کارآمد ثابت ہو سکتا ہے۔
  • ویژول اسٹوڈیو کے ٹاسکس اور تھریڈز ونڈوز ملٹی تھریڈ یا غیر مطابقت پذیر کوڈ کو ڈیبگ کرنے کے لیے بہت سی معلومات فراہم کرتی ہیں۔
  • PLinq ایک زبردست ٹول ہے، لیکن ہو سکتا ہے کہ اس میں آپ کے ڈیٹا سورس کے بارے میں کافی معلومات نہ ہوں، لیکن اسے تقسیم کرنے کے طریقہ کار کا استعمال کرتے ہوئے ٹھیک کیا جا سکتا ہے۔
  • جاری رکھنا ...

ماخذ: www.habr.com

نیا تبصرہ شامل کریں