Sobes.tech
Middle

Как да създадете два нишки в Dart в контекста на Flutter?

sobes.tech AI

Отговор от AI

В Dart/Flutter се използват изолати (Isolates) за създаване на независими потоци за изпълнение. Те представляват отделни задачи с собствено отделено пространство за памет, което предотвратява проблеми с конкурентен достъп до данни и блокиране на основния поток (UI поток).

Има няколко начина за създаване и взаимодействие с изолатите:

  1. Isolate.spawn(): Най-ниското ниво за създаване на изолат. Изисква подаване на функция от по-високо ниво или статичен метод на клас за изпълнение в новия изолат и порт за обмен на съобщения.

    import 'dart:isolate';
    
    // Функция, която се изпълнява в изолата
    void isolateEntry(SendPort sendPort) {
      // Изпълняваме дълга операция
      int result = 0;
      for (int i = 0; i < 1000000000; i++) {
        result += i;
      }
      // Изпращаме резултата обратно на основния изолат
      sendPort.send(result);
    }
    
    void main() async {
      ReceivePort receivePort = ReceivePort();
      // Създаваме нов изолат
      Isolate isolate = await Isolate.spawn(isolateEntry, receivePort.sendPort);
    
      // Следим съобщенията от изолата
      receivePort.listen((message) {
        print('Резултат от изолата: $message');
        // Спираме изолата след получаване на съобщение
        isolate.kill();
      });
    
      print('Основният поток продължава работа...');
    }
    
  2. compute(): Улесена функция от пакета flutter/foundation, която използва Isolate.spawn() под капота. Идеална за прости фонови задачи, които не изискват сложна комуникация с изолата.

    import 'package:flutter/foundation.dart';
    
    // Функция, която се изпълнява с помощта на compute
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      print('Започваме тежки изчисления на фона...');
      // Изпълняваме функцията в отделен изолат
      int result = await compute(heavyComputation, 1000000000);
      print('Резултат от изчисленията: $result');
      print('Основният поток продължава след изчисленията.');
    }
    
  3. Isolate.spawnUri(): Използва се за стартиране на код от отделен файл dart в нов изолат.

Взаимодействие между изолатите:

Изолатите обменят данни само чрез изпращане на съобщения чрез екземпляри SendPort и ReceivePort. Това са атомарни операции, които не изискват блокировки или други механизми за синхронизация.

  • SendPort: Използва се за изпращане на съобщения от един изолат към друг.
  • ReceivePort: Използва се за слушане на входящи съобщения.

При прехвърляне на обекти между изолатите те трябва да бъдат "прехвърляеми" (transferable), което включва:

  • Простите типове данни (int, double, String, bool)
  • Списъци и речници, съдържащи прехвърляеми типове
  • Някои специфични за Flutter типове, които могат да бъдат сериализирани

Обектите, които не са прехвърляеми, ще бъдат копирани, а последващите им промени в един от изолатите няма да се отразят в другия.

Ключови моменти:

  • Изолатите не могат директно да се обръщат към паметта на другите.
  • Те нямат достъп до общо състояние, включително променливи, класове и widget-и на основния изолат (UI поток).
  • Взаимодействието между изолатите става асинхронно.
  • В Flutter е важно правилно да управлявате техния жизнен цикъл (например, използвайки isolate.kill() при необходимост).

Използването на изолати позволява изпълнение на дълги или ресурсоемки операции във фонов режим, предотвратявайки блокиране на потребителския интерфейс и осигурявайки плавна работа на приложението.