Sobes.tech
Middle

Jak utworzyć dwa wątki w Dart w kontekście Flutter?

sobes.tech AI

Odpowiedź od AI

Aby tworzyć niezależne wątki wykonawcze w Dart/Flutter, używa się Izolatów (Isolates). Są to oddzielne zadania z własną dedykowaną przestrzenią pamięci, co zapobiega problemom z dostępem współbieżnym do danych i blokadom głównego wątku (wątku UI).

Istnieje kilka sposobów tworzenia i interakcji z izolatorami:

  1. Isolate.spawn(): Niskopoziomowa metoda tworzenia izolatora. Wymaga przekazania funkcji na poziomie wyższym lub statycznej metody klasy do wykonania w nowym izolatorze oraz portu do wymiany wiadomości.

    import 'dart:isolate';
    
    // Funkcja wykonywana w izolatorze
    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('Wynik z izolatora: $message');
        isolate.kill();
      });
      print('Główny wątek kontynuuje pracę...');
    }
    
  2. compute(): Uproszczona funkcja z pakietu flutter/foundation, która używa Isolate.spawn() pod maską. Idealna do prostych zadań w tle, które nie wymagają skomplikowanej interakcji z izolatem.

    import 'package:flutter/foundation.dart';
    
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      int result = await compute(heavyComputation, 1000000000);
      print('Wynik obliczeń: $result');
    }
    
  3. Isolate.spawnUri(): Używany do uruchamiania kodu z osobnego pliku Dart w nowym izolatorze.

Interakcja między izolatorami:

Izolatory wymieniają dane tylko poprzez wysyłanie wiadomości za pomocą instancji SendPort i ReceivePort. Są to operacje atomowe.

  • SendPort: Używany do wysyłania wiadomości z jednego izolatora do drugiego.
  • ReceivePort: Używany do nasłuchiwania nadchodzących wiadomości.

Podczas przesyłania obiektów między izolatorami, muszą one być "przesywalne" (transferable), co obejmuje:

  • Proste typy danych (int, double, String, bool)
  • Listy i mapy zawierające przesywalne typy
  • Niektóre specyficzne typy Flutter, które mogą być serializowane

Obiekty, które nie są przesywalne, będą kopiowane, a ich późniejsze zmiany w jednym z izolatorów nie będą odzwierciedlone w drugim.

Kluczowe punkty:

  • Izolatory nie mogą bezpośrednio odwoływać się do pamięci innych.
  • Izolatory nie mają dostępu do wspólnego stanu, w tym zmiennych, klas i widżetów głównego izolatora (wątku UI).
  • Komunikacja między izolatorami odbywa się asynchronicznie.
  • Przy pracy z izolatorami w Flutter ważne jest odpowiednie zarządzanie ich cyklem życia (np. użycie isolate.kill() w razie potrzeby).

Użycie izolatorów pozwala na wykonywanie długotrwałych lub zasobożernych operacji w tle, zapobiegając zawieszaniu interfejsu użytkownika i zapewniając płynne działanie aplikacji.