برنامه نویس اندروید ( جاوا یِ سابق, فلاتر الان )
توضیحاتی کوتاه در مورد isolate و multi thread در فلاتر
شاید برای شما این اتفاق افتاده باشه که تو یک بخشی از پروژه مجبور به انجام یک کار سنگین بشید
مثلا تبدیل یه فایل با حجم بالا, به آرایه ای از Byte یا دانلود یه فایل با حجم بالا و یا انجام یک سری محاسبات پیچیده که ممکنه منابع زیادی رو اشغال کنه !
خوب همه ما میدونیم که کل اپ ما تو یک thread ( حالا فارسیش میشه نخ ولی در طول این مقاله از کلمه رشته برای اشاره به Thread استفاده میکنیم ) داره عمل میکنه که اصطلاحا بهش Main-Thread میگن و میشه گفت که اکثریت اتفاقات بر روی این رشته میفته از جمله رندر کردن UI و اگه وقفه ای تو این عمل پیش بیاد ممکنه که به UX یا همان تجربه کاربری ما صدمه بزنه و حس خوبی به کاربر نده ...
این دقیقا مشکلی بود که من تو یکی از پروژه هام باهاش روبرو شدم و بعد از خوندن چندتا مقاله و آزمون خطا بهرین راه رو ( حداقل از نظر خودم ) انتخاب کردم که همون isolate هستش !
بریم یه سناریو طراحی کنیم تا تو عمل بهتر متوجه مشکل و راه حلش بشیم .
تو سایت StackOverflow مثال واقعی تری در مورد isolate پیاده کردم که ممکنه تو درک بهتره این موضوع کمکتون کنه
تصور کنین یه حلقه for داریم که میخوایم از ۰ تا ۴۰۰۰ رو برامون طی کنه !
خب از اونجایی که این عمل تقریبا سنگین هستش, پس احتمال اینکه چند ثانیه وقفه تو اجرای برنامه داشته باشیم زیاده و اصطاحا یه Freeze کوتاه داشته باشیم !
کلاس Home خودمون رو می سازیم به ساده ترین شکل ممکن که شامل یک CircularProgressIndicator برای نشون دادن وقفه و یک دکمه برای اجرای حلقه.
و یه متد خیلی ساده برای حلقه for و متصل کردنش به onPressed دکمه
خب !
پارامتر stop متد بالا رو روی ۴۰۰۰ تنظیم میکنیم بعد به دکمه وصلش میکنیم که بعد از کیلک با همچین صحنه ای روبرو میشم ..
همون طور که تو gif بالا میبینید پروگرس برای چند ثانیه فریز شد و اون دقیقا لحظه کلیک روی دکمه بود .
خب بریم سراغ راه حل
برای حل این مشکل راه های زیادی وجود داره ولی ما از isolate استفاده میکنیم تا به هدفمون که یه نگاه کلی به isolate هستش رو برسیم.
طبقه گفته خود فلاتر isolate ها مموری خودشون رو دارن و جدای از مموری اختصاص داده شده به اپ عمل میکنن و این باعث میشه که Main-Thread به مشکل نخوره ...
تو این مقاله از Isolate.spawn استفاده میکنیم که ساده ترین و پایه ای ترین روش هستش
بریم سراغ کد :
اول کد هارو مینویسم بعد توضیح میدم !
اول متد isolate رو ایجاد میکنیم به شکل زیر
تو این متد ما یه نمونه از کلاس ReceiverPort میسازیم تا بتونیم به مقدار بازگشتی متدی که داخل isolate درحال اجراس دسترسی داشته باشیم !
( من مثال رو یکم تغییر دادم که این حالت رو هم بتونیم بررسی کنیم و زیاد ساده نباشه )
کلاس isolate یه متد static به اسم spawn داره که نوع بازگشتی isolate هستش و به صورت Asynchronous نوشته شده (Future)
همون طور که تو خط کد های بالا میبینید متد for یا همون expensiveCalculate رو به عنوان پارامتر اول به spawn پاس دادیم و پارانتزی جلوش قرار ندادیم که یعنی این متد زمانی که خود isolate فراخوانی بشه اجرا میشه حتما حواستون به این باشه !
و پارامتر دوم لیستی از نوع داده dynamic هستش
خب حالا چرا لیست ؟
تو این روش ما نمیتونیم برای متد for بیشتر از یک پارامتر در نظر بگیریم و این در حالیه که ما علاوه بر مقدار stop sendPort هم باید پاس بدیم
این برای زمانی که مقدار بازگشتیه متد برامون مهم باشه در غیره این صورت میتونیم فقط مقدار stop حلقه رو که int هستش پاس بدیم !
ولی اینجا ما میخوایم وقتی حلقه تموم شد یه مقدار String برامون برگرده پس حتما باید از port پراپرتی sendPort که یه getter از نوع کلاس SendPort هست و این getter در داخل کلاس ReciverPort در دسترس هستش و ما قبلا تعریفش کردیم رو به عنوان پارامتر پاس بدیم و همچنین مقدار stop حلقه رو...
چی گفتم !؟ (((((:
پس برای همین از لیست استفاده میکنیم .
نکته : پارامتر دوم spawn از نوع Object که یعنی میتونیم کلاس یا مدل هم بهش پاس بدیم و کد تمیز تری بنویسیم
نکته بعدی مقدار جنریک spawn هستش که اگه به قطعه کد پایین نگاه کنین دقیقا مشابه با پارامتر ورودی متد for هستش و بایدم باشه
و در آخر قرار دادن نمونه گرفته شده از کلاس ReciverPort در حالت listen هستش که بتونیم مقدار نهایی رو از متد for بگیریم .
این از کد بالا چیز دیگه ای برای توضیح نداره :)
حالا کد های داخل متد for :
داخل متد for مقادیر stop و sendPort رو میگیریم و داخل یه متغیر ذخیره میکنیم !
و در نهایت نتیجه متد رو که تو مثال ما مقدار done بود رو با sendPort به listener برمیگردونیم !
نکته : حتما در آخر بعد از اینکه کارتون با isolate و port تموم شد هردو رو close کنین .
نکته بعدی اینکه این متد دیگه نیاید داخل اسکپ هیچ کلاسی باشه و به اصطلاح Global-Function یا همون High-Level باید باشه
با این روش دیگه عملیات های سنگین تاثیری تو UI و UX برنامتون نمیزاره و وقفه تو روند اجرای برنامه ایجاد نیمشه .
خب امیدوارم که تونسته باشم حق مطلب رو درست ادا کنم و براتون مفید بوده باشه !
و در نهایت ممنون از وقتی که گذاشتید برای مطالعه این مقاله
موفق باشید . :)
مطلبی دیگر از این انتشارات
اصل چهارم پیاده سازی SOLID با کدهای دارت برای فریم ورک Flutter
مطلبی دیگر از این انتشارات
اجرای برنامه های فلاتر در لینوکس
مطلبی دیگر از این انتشارات
نسخه ۲ فلاتر معرفی شد!