Flutter isolates - bilishingiz kerak bo'lgan hamma narsa
"Izolyatsiya" so'zi siz uchun yangi bo'lmasligi kerak - pandemiya boshlanganidan beri u mashhur so'zga aylandi. Ehtimol, siz bu atamani Flutterga nisbatan ishlatilganini ham eshitgandirsiz, ammo "isolates" o'zi nima? Dart kodingizni qanday "run" qiladi? Isolatesni qanday amalga oshirasiz? Bu savollarning barchasiga ushbu maqolada javob beriladi.
Agar siz Flutterda yangi bo'lsangiz, bu maqola siz uchun, chunki men ko'plab tushunchalarni soddalashtirishga harakat qildim. Ammo, agar siz tajribali dasturchi bo'lsangiz, ushbu maqola ham foydali bo'lishi mumkin. Flutter izolatlari kontseptsiyasini tushuntirishga yordam berish uchun biz kofe kioskining analogiyasidan foydalanamiz .
Dartda kodning bajarilishi
Dart - bu bir oqimli til (single thread). Bu nimani anglatishini tushunish uchun birinchi navbatda oqimlar nima ekanligini tushunishimiz kerak.
Oqim (thread) nima?
Oqim - bu jarayonning birligi. Bu asosiy jarayondan mustaqil ravishda rejalashtirilishi va bajarilishi uchun mo'ljallangan kichik ko'rsatmalar to'plami.
Hisoblashda jarayon atamasi bajarilayotgan dasturning holatini bildiradi.
Endi sizni bir narsa qiziqtirayotgan bo‘lishi mumkin: Agar Dart bitta oqimli til bo‘lsa, Flutter-dagi asinxron vazifalar (masalan, HTTP so'rovi orqali ma’lumotlarni olish) ilovaning boshqa faoliyatiga to‘sqinlik qilmasdan qanday qilib optimal tarzda bajarilishi mumkin? Xo'sh, bu ikki xil tushunchaga olib keladi, konkurentlik (concurrency) va parallellik (parallelism), birinchisi asinxron vazifalar uchun, ikkinchisi esa izolyatsiyada ishlaydigan vazifalar uchun. Ushbu tushunchalarni tushunish uchun keling, ikkita stsenariyni tayyorlaymiz.
1-stsenariy (concurrency)
Siz qahvaxonada kofe ichmoqchisiz. (Kofe do'konini faqat eshikli yopiq xona deb hisoblang.) Uzun navbat bor, lekin siz o'z navbatingizni kutishga qaror qildingiz. Barista (kofe ichimliklar tayyorlovchi va xizmat ko'rsatuvchi shaxs) navbatdagi har bir keyingi mijozning buyurtmalarini birin-ketin qabul qiladi.
Aytaylik, barista A va barista B xizmarlari o'rtasida vaqtli farq bor. Mijozlar barista B ni buyurtmalar qabul qilishini kutishi va navbat bilan siljishlari kerak. Barista A va barista B xizmatlari o'rasidagi farq mijozlarning harakatlariga boshqa tarzda to'sqinlik qilmaydi, chunki ular hali ham bir-birlari bilan gaplashishi, o'zaro tanalari bilan aloqa qilishlari mumkin va xkz.
Dartdagi asinxron kod ham xuddi shunday ishlaydi: await
kalit so'ziniasync
funksiyasida ishlatganingizda, dasturning boshqa qismlari ishlashda davom etadi va async
vazifa tugagach, kodning keyingi qatoridan davom etadi. Vazifa (Task) - bu kerak bo'lganda async
dasturingizning turli qismlari (voqealarni qayta ishlash) o'rtasida o'tadigan Dart hodisalari tsikli. Quyidagi kod namunasi buni amalda ko'rsatadi.
void asyncTask() async { /// Ushbu vazifa o'tishi uchun 5 sekund kutiladi final result = await Future.delayed(Duration(seconds: 5)); /// So'ng, kodning keyingi qismlari ishlashga o'tadi /// Kutish davomida, bu kodning boshqa qismlarini ishlashdan bloklamaydi print(result); }
2-stsenariy (parallelism)
Bu safar siz va do'stingiz qahvaxonaga borishga qaror qildingiz, lekin birinchi do'konda navbat juda ko'p va kechikishlar juda ko'p. Shunday qilib, do'stingiz sizni birinchi qahvaxonada qoldirib, keyingi qahvaxonaga borishga qaror qiladi.
Bu stsenariyda aytishimiz mumkinki, siz birinchi qahvaxonada kofe olishingiz va do'stingiz keyingi qahvaxonada qahva olish jarayoni parallel ravishda sodir bo'lmoqda, chunki birinchi qahvaxonada nima sodir bo'lishidan qat'i nazar, keyingi qahvaxonaga ta'sir qilmaydi.
Bir qator dasturlarni parallel ravishda ishga tushirish uchun izolyatsiyalar ishga tushadi. Ammo ular buni qanday qilishlarini muhokama qilishdan oldin, birinchi navbatda Dartdagi izolyatsiya nima ekanligini tushunishimiz kerak.
Izolyatsiya nima?
Gapirishdan oldin shuni ta'kidlash kerakki, Dart dasturlari "default" holat bo'yicha izolyatsiyalarda ishlaydi. Dartdagi izolyatsiyani kofe kioski ichidagi kofe ichimliklar tayyorlash va qahva buyurtma qilish kabi tadbirlar o'tkaziladigan kichkina xona deb o'ylab ko'ring. Izolyatsiya mashinadagi kichik bo'shliqqa o'xshaydi, uning xotirasi (qisqa muddatli asosda kerak bo'ladigan ma'lumotlarni saqlaydigan tizim) va kodni qayta ishlovchi voqealar sikli (event loop) da ishlaydigan bitta oqim.
Dart "multithreading"
Esingizda bo'lsa, biz Dartni bir oqimli til deb aytgan edik. Ammo bizda ishlash uchun juda og'ir hisoblash kodi bo'lsa-chi? Bunday holda, vazifani asinxron usulda bajarish, ehtimol, ilovada kechikishga olib keladi, shuning uchun bizga bu vazifani boshqa ish oqimlarida bajarish usuli kerak.
Dartda multithreadingni amalga oshirish uchun biz yangi izolyatsiyani yaratishimiz kerak. Shuni ta'kidlash kerakki, Dartda izolyatsiyalar xotirani boshqa izolatlar bilan baham ko'rmaydi. Aksincha, ular bir-biridan butunlay ajratilgan (oh, bechora izolyatsiyalar ...) , va ularning nomi aynan shundan kelib chiqqan.
Yangi izolyatsiyada o'z xotirasi va voqea tsikllari bo'ladi. Buni mustaqil Dart dasturini ishga tushirish deb tasavvur qiling. Yaxshi yangilik bor: ikkita izolyatsiya xabarlarni oldinga va orqaga uzatish orqali bir-biri bilan muloqot qilishi mumkin. Quyida buning misoli keltirilgan.
Dartda boshqa izolyatsiyani yaratishning ikki yo'li mavjud: Isolate.spawn()
funktsiya va compute()
funktsiya.
Isolate.spawn() yordamida yangi izolyatsiya yaratish
Keling, Flutter izolyatsiyasining ba'zi misollarini ko'rib chiqaylik. Izolyatsiyani yaratishning birinchi usuli Isolate.spawn()
dan foydalanishdir. Biz birinchi parametr sifatida ishlatmoqchi bo'lgan usulga o'tamiz, ikkinchi argument esa biz izolyatsiyaga o'tmoqchi bo'lgan parametrdir.
Izolyatsiyaga uzatiladigan funksiya spawn()
yuqori darajadagi funksiya *(sinf chegarasida bo‘lmagan funksiya) yoki statik usul bo‘lishi kerak.
Mana, izolyatsiyani yaratish uchun kod:
import 'dart:isolate'; void main() { Isolate.spawn<IsolateModel>(heavyTask, IsolateModel(355000, 500)); } void heavyTask(IsolateModel model) { int total = 0; /// Belgilangan miqdordagi iteratsiyalarni bajaradi for (int i = 1; i < model.iteration; i++) { /// Har bir indexni ko'paytiruvchiga ko'paytiradi va jamini hisoblaydi total += (i * model.multiplier); } log("FINAL TOTAL: $total"); } class IsolateModel { IsolateModel(this.iteration, this.multiplier); final int iteration; final int multiplier; }
Ikki izolyatsiya o'rtasida muloqot qilish
Ikki izolyatsiya o'rtasidagi aloqani portlar ( ReceivePort
va SendPort
) orqali xabarlar yoki qiymatlarni yuborish orqali amalga oshirish mumkin. Ushbu portlar Stream
kabi ishlaydi. Aslida Stream
mavhum sinfidan ReceivePort
foydalanadi.
SendPort-ni ReceivePort-dan ReceivePort-ning sendPort
qabul qilish usuli (getter) ni chaqirish orqali yaratish mumkin . Bu xabarlar ReceivePort-ga yuboriladigan vositadir. Boshqa tomondan, ReceivePort o'zining SendPort-dan xabarlarni tinglaydi.
Ushbu jarayonning vizual tasvirini ko'rish uchun quyidagi rasmga qarang:
Ikki izolyatsiya o'rtasida xabar qanday yuborilishi tasvirlangan rasmli illyustratsiya. SenderPort SendPort orqali xabar sifatida yuborilishi mumkin, bu esa qabul qiluvchiga qayta aloqa qilish uchun ruxsat beradi.
Mana, izolyatsiyalar orasidagi aloqani ko'rsatadigan kod parchasi. Diqqat - kod biroz chalkash. Bu qanday ishlashini tushunishingizga yordam berish uchun ba'zi izohlar qo'shdim. Kodni yozish va ishga tushirish ham yordam beradi. Kodni o'qiyotganda, Maykni do'stingizning ismi deb hisoblang.
import 'dart:isolate'; void main() { createIsolate(); } Future createIsolate() async { /// Where I listen to the message from Mike's port ReceivePort myReceivePort = ReceivePort(); /// Spawn an isolate, passing my receivePort sendPort Isolate.spawn<SendPort>(heavyComputationTask, myReceivePort.sendPort); /// Mike sends a senderPort for me to enable me to send him a message via his sendPort. /// I receive Mike's senderPort via my receivePort SendPort mikeSendPort = await myReceivePort.first; /// I set up another receivePort to receive Mike's response. ReceivePort mikeResponseReceivePort = ReceivePort(); /// I send Mike a message using mikeSendPort. I send him a list, /// which includes my message, preferred type of coffee, and finally /// a sendPort from mikeResponseReceivePort that enables Mike to send a message back to me. mikeSendPort.send([ "Mike, I'm taking an Espresso coffee", "Espresso", mikeResponseReceivePort.sendPort ]); /// I get Mike's response by listening to mikeResponseReceivePort final mikeResponse = await mikeResponseReceivePort.first; log("MIKE'S RESPONSE: ==== $mikeResponse"); } void heavyComputationTask(SendPort mySendPort) async { /// Set up a receiver port for Mike ReceivePort mikeReceivePort = ReceivePort(); /// Send Mike receivePort sendPort via mySendPort mySendPort.send(mikeReceivePort.sendPort); /// Listen to messages sent to Mike's receive port await for (var message in mikeReceivePort) { if (message is List) { final myMessage = message[0]; final coffeeType = message[1]; log(myMessage); /// Get Mike's response sendPort final SendPort mikeResponseSendPort = message[2]; /// Send Mike's response via mikeResponseSendPort mikeResponseSendPort.send("You're taking $coffeeType, and I'm taking Latte"); } } }
Yuqoridagi kodni haddan tashqari ortiqcha ishlab chiqilgan deb aytish mumkin, buning sababi shundaki, izolyatsiyalar faqat asosiy oqimda kechikishga olib kelmaslik uchun boshqa oqimda bajarilishi kerak bo'lgan og'ir hisoblash ishlarini bajarishda ishlatilishi kerak. Ya'ni, yuqoridagi kodning maqsadi turli xil izolyatsiyalar bir-biri bilan qanday aloqa qilishini ko'rsatishdir.
Izolyatsiya o'zining voqealar siklidagi barcha hodisalarni amalga oshirganda, Dart avtomatik ravishda izolyatsiyani o'ldiradi. Izolyatsiyani qo'lda o'ldirish uchun isolate.kill()
dan foydalaning. Izolyatsiyani to'xtatib turish uchun isolate.pause()
dan foydalaning . To'xtatilgan izolyatsiyani davom ettirish uchun isolate.resume()
dan foydalaning.
compute() yordamida yangi izolyatsiya yaratish
Yangi izolyatsiyani yaratishning eng oson yo'li - compute()
dan foydalanish, garchi o'zaro kelishuv shundaki, siz ko'plab moslashuvchan Isolate.spawn()
bergan imkoniyatlarni yo'qotasiz.
compute<Q, R>()
ikkita umumiy turni qabul qiladi: Q
bu izolyatsiya funksiyasi parametr turi va R
izolyatsiya funksiyasining qaytish turi. Quyidagi kod parchasi buni qanday amalga oshirishni ko'rsatadi:
import 'package:flutter/foundation.dart'; void main() { compute<IsolateModel, void>(heavyTask, IsolateModel(355000, 500)); } void heavyTask(IsolateModel model) { int total = 0; /// Performs an iteration of the specified count for (int i = 1; i < model.iteration; i++) { /// Multiplies each index by the multiplier and computes the total total += (i * model.multiplier); } log("FINAL TOTAL: $total"); } class IsolateModel { IsolateModel(this.iteration, this.multiplier); final int iteration; final int multiplier; }
Xulosa
Xulosa qilib aytadigan bo'lsak, Dart-da "multithread" larni o'tkazish mumkin, garchi Dart bir-treadli til bo'lsa ham. Bitta ogohlantirish bor: Dart ilovalari allaqachon juda tez va yuqori darajada optimallashtirilgan va ko'pincha siz izolyatsiyadan foydalanishingiz shart emas. Uni faqat og'ir hisoblash ishlarini bajarish kerak bo'lganda foydalaning. Shuningdek, biz voqea tsikllari nima ekanligini va ular Dartda qanday ishlashini muhokama qildik va izolyatsiyalar xotirani baham ko'rmasa ham, qanday qilib muloqot qilishini ko'rib chiqdik. Sog' bo'ling!
Manba: https://blog.codemagic.io/understanding-flutter-isolates/
Telegram: @flutterblogs