اپلیکیشن های فلاتر ممکن است در بعضی موارد نیاز به انجام پردازش های سنگین داشته باشند که این کار باعث میشود کاربر در هنگام استفاده از برنامه با مشکلات مختلفی روبرو شود.
برای حل این مشکل در فلاتر از مفهومی به نام Isolate استفاده میکنیم. برای کار با این مورد نیاز به بررسی یک سری موارد از قبل داریم.
در نظر داشته باشید که به طور کلی مبحث برنامه نویسی Multi Thread جزو مباحث پیشرفته در آموزش برنامه نویسی موبایل میباشد و اگر هنوز به مبانی ابتدایی تسلط ندارید شاید این مفاهیم برای شما پیچیده باشد.
در برنامه نویسی کامپیوتر، یک thread یا نخ یک واحد اجرای مستقل است که می تواند در یک برنامه اجرا شود.
هر thread دارای حافظه و شمارنده برنامه خود است. این به این معنی است که هر نخ می تواند داده های خود را داشته باشد و بدون اینکه بر نخ های دیگر تأثیر بگذارد، کار خود را انجام دهد.
threadها می توانند مزایای زیر را برای برنامه ها به ارمغان بیاورند:
با این حال، نخ ها می توانند خطرات زیر را نیز برای برنامه ها به همراه داشته باشند:
برنامه نویسان باید قبل از استفاده از نخ ها، خطرات و مزایای آن را درک کنند.
در اینجا چند نمونه از کاربردهای thread ها آورده شده است:
در زبان های برنامه نویسی مختلف، روش های مختلفی برای ایجاد و مدیریت نخ ها وجود دارد.
برنامه نویسی چند نخی (Multithreading) یک تکنیک برنامه نویسی است که به برنامه نویسان این امکان را می دهد تا چندین کار را به طور همزمان در یک برنامه اجرا کنند. هر کار در یک نخ جداگانه اجرا می شود.
برنامه نویسان میتوانند با استفاده از multi thread، برنامههایی بنویسند که در حالت موازی چندین عملیات را انجام میدهند، که میتواند به سرعت اجرای بهتر و بهتر برنامه را به شکلی که کاربر بهترین تجربه را دریافت میکند به شکلی که کاربر بهترین تجربه را دریافت میکند، کمک کند.
تقریبا هر نرم افزاری که نیاز به انجام پردازش های مختلف به شکل موازی باشد از این تکنیک استفاده میکند.
برای مثال در یک مرورگر اینترنتی در هر تب میتوانید به شکل جداگانه فایل های مختلفی را دانلود کنید.
در واقع هر فرآیند یا Process میتواند چندین Thread را ایجاد کند که هرکدام قطعه کد و یا دستور خاصی را اجرا میکنند. بدین ترتیب شاهد این خواهیم بود که برنامه ها با سرعت بیشتری در کامپیوترها اجرا میشوند.
فرآیندها معمولاً به صورت ترتیبی اجرا میشوند – یک فرآیند پس از دیگری. با این حال، این میتواند منجر به مسدود شدن سختافزار در هنگام انجام وظایف طولانی شود که چندان مطلوب نیست.
اگر فرآیند دیگری نیاز به اجرا داشته باشد، باید منتظر نوبت خود باشد. در مورد multi thread، چندین Thread به طور نسبی به طور همزمان پردازش میشوند. اگرچه پردازش واقعاً همزمان اتفاق نمیافتد، اما در حال حاضر امکانپذیر است.
اما حتی “شبه همزمانی” نیز باعث افزایش عملکرد میشود. سیستم نخها را به گونهای هوشمندانه سازماندهی و محاسبه میکند که کاربر آن را پردازش همزمان میپندارد.
این نوع همزمانی نباید با آنچه یک پردازنده چند هستهای میتواند انجام دهد اشتباه گرفته شود. اگر سیستم دارای چندین میکروپروسسور باشد، چندین فرآیند به طور همزمان پردازش خواهند شد.
در فلاتر هر برنامه یک پروسه دارد که در داخل آن پروسه شما میتوانید از روشهای مختلفی برای اجرای دستورالعملهای مختلف بهطور همزمان استفاده کنید.
زمانی که برنامهی فلاتر اجرا میشود، یک main thread یا isolate برای برنامهی شما ایجاد میشود که کدهای شما داخل اون thread یا isolate اجرا میشن که بهش main thread یا UI thread گفته میشود.
در واقع isolate همان Thread در سایر زبانها هست که در زبان برنامه نویسی دارت به آن isolate میگویند.
بین isolate ها حافظهی مشترکی وجود ندارد.
برای برقراری ارتباط بین isolate ها از port و message ها برای تبادل پیام استفاده میشود.
در برنامههای سادهی فلاتر معمولاً یک isolate وجود دارد، اما درصورتی که قصد انجام عملیاتی طولانی و سنگین رو داشته باشید، برای اینکه عملکرد UI Thread مختل نشه، بهتر هست که برای انجام آن یک Isolate جدید ایجاد کنید.
برای اینکه بهتر مزیت استفاده از Isoalte در فلاتر را درک کنید با یک مثال این کار و انجام میدهیم.
ابتدا یک انیمیشن ایجاد میکنیم و داخل برنامه قرار میدهیم.
این انیمیشن یک ویجت Container را از سمت چپ صفحه به سمت راست حرکت میدهد و این کار تکرار میشود.
کدهای این بخش از انیمیشن به شکل زیر است.
class AnimatedContainer extends StatefulWidget {
@override
_AnimatedContainerState createState() => _AnimatedContainerState();
}
class _AnimatedContainerState extends State<AnimatedContainer> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller = AnimationController(vsync: this,duration: Duration(seconds: 2));
_animation = Tween<Offset>(begin: Offset.zero,end: Offset(6,0)).animate(_controller);
_controller.repeat();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SlideTransition(position: _animation,
child: Container(
width: 50,
height: 50,
color: Colors.redAccent,
),),
SizedBox(height: 40,),
ElevatedButton(onPressed: ()async{
TestTask();
},
],
),
),
),
);
}
}
در اینجا برای شبیه سازی یک عملیات پردازش سنگین متد TestTask را هنگام کلیک کردن روی دکمه فراخوانی میکنیم.
این متد یک حلقه بسیار طولانی است که انجام آن نیاز به منابع زیادی دارد.
Future<int> TestTask()async{
var result = 0;
for(int i = 0; i<1450000000;i++){
result++;
}
return result;
}
در تصویر زیر مشاهده میکنید که زمان فراخوانی این متد انیمیشن مورد نظر دچار مشکل میشود و در صفحه ثابت میماند تا عملیات متد TestTask تکمیل شود.
علت این مورد این است که هر دو کار در حال پردازش در Isolate اصلی برنامه است و رابط کاربری با مشکل روبرو میشود.
برای حل این مشکل باید عملیات هایی که پردازشی سنگینی نیاز دارند را در یک Isolate دیگر قرار دهیم.
حالا مثال و قبلی با استفاده از Isolate بررسی میکنیم.
کدهای زیر را در نظر بگیرید.
Future<int> TestTask(SendPort sendPort)async{
var result = 0;
for(int i = 0; i<1450000000;i++){
result++;
}
sendPort.send('Result: $result');
return result;
}
ابتدا به متدی که داشتیم یک پارامتر ورودی از کلاس SendPort اضافه میکنیم.
همانطور که گفتیم راه ارتباطی بین Isolateها در فلاتر از طریق ارسال پیام میباشد. در صورتی که بخواهید به یک Isolate دیگر پیامی ارسال کنید از SendPort کمک میگیریم.
در اینجا نیز نتیجه نهایی پردازش را از همین طریق به Isolate اصلی برنامه ارسال میکنیم.
کدهای رویداد کلیک دکمه داخل صفحه را هم به شکل زیر تغییر میدهیم.
var rec_port = ReceivePort();
var isolates = await Isolate.spawn(TestTask,rec_port.sendPort);
rec_port.listen((message) {
print("Message: $message");
});
ابتدا یک ReceivePort تعریف میکنیم تا بتوانیم پیام هایی که از طریق Isolate جدید ارسال میشود را دریافت کنیم.
برای ساخت Isolate جدید هم از متد Isolate.spawn استفاده میکنیم.
پارامتر اول این متد همان تابع پیاده سازی شده است که میخواهیم در یک Thread جدید قرار گیرد و پارامتر دوم هم در واقع آرگومان های مورد نیاز تابع TestTask میباشد که از همان ReceivePort مقدار SendPort را قرار داده ایم.
در نهایت هم برای اینکه بتوانیم پیام های ارسالی را دریافت کنیم برای ReceivePort یک Listener تعریف میکنیم که از نوع استریم است.
با اجرای این کد بدین شکل مشاهده میکنید که دیگر انیمیشنی که در رابط کاربری اپلیکیشن فلاتر وجود دارد دچار مشکل نمیشود و به شکل همزمان نیز عملیات پردازش انجام میشود.
در صورتی که میخواهید به شکل حرفه ای طراحی اپلیکیشن را یاد بگیرید میتوانید از دوره آموزش فلاتر پروژه محور استفاده نمایید.