Sobes.tech
Middle

Jak vytvořit dva vlákna v Dart v kontextu Flutter?

sobes.tech AI

Odpověď od AI

V Dart/Flutter se pro vytváření nezávislých toků provádění používají izoláty (Isolates). Ty představují samostatné úlohy s vlastním vyhrazeným prostorem paměti, což zabraňuje problémům s konkurenčním přístupem k datům a blokování hlavního vlákna (UI vlákna).

Existuje několik způsobů, jak vytvářet a komunikovat s izoláty:

  1. Isolate.spawn(): Nejnižší úroveň vytváření izolátu. Vyžaduje předání funkce vyšší úrovně nebo statické metody třídy, která se má vykonat v novém izolátu, a portu pro výměnu zpráv.

    import 'dart:isolate';
    
    // Funkce, která se vykonává v izolátu
    void isolateEntry(SendPort sendPort) {
      // Provádíme nějakou dlouhou operaci
      int result = 0;
      for (int i = 0; i < 1000000000; i++) {
        result += i;
      }
      // Odesíláme výsledek zpět hlavnímu izolátu
      sendPort.send(result);
    }
    
    void main() async {
      ReceivePort receivePort = ReceivePort();
      // Vytvoření nového izolátu
      Isolate isolate = await Isolate.spawn(isolateEntry, receivePort.sendPort);
    
      // Posloucháme zprávy od izolátu
      receivePort.listen((message) {
        print('Výsledek z izolátu: $message');
        // Ukončíme izolát po obdržení zprávy
        isolate.kill();
      });
    
      print('Hlavní vlákno pokračuje v práci...');
    }
    
  2. compute(): Zjednodušená funkce z balíčku flutter/foundation, která používá Isolate.spawn() na pozadí. Ideální pro jednoduché úlohy na pozadí, které nevyžadují složitou komunikaci s izolátem.

    import 'package:flutter/foundation.dart';
    
    // Funkce, která se vykonává pomocí compute
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      print('Začínáme těžké výpočty na pozadí...');
      // Spouštíme funkci v samostatném izolátu
      int result = await compute(heavyComputation, 1000000000);
      print('Výsledek výpočtů: $result');
      print('Hlavní vlákno pokračuje po výpočtech.');
    }
    
  3. Isolate.spawnUri(): Používá se ke spuštění kódu z samostatného souboru dart v novém izolátu.

Interakce mezi izoláty:

Izoláty si vyměňují data pouze prostřednictvím odesílání zpráv přes instance SendPort a ReceivePort. Jedná se o atomické operace, které nevyžadují zámky nebo jiné synchronizační mechanismy.

  • SendPort: Používá se k odesílání zpráv z jednoho izolátu do druhého.
  • ReceivePort: Používá se ke sledování příchozích zpráv.

Při přenosu objektů mezi izoláty musí být objekty "přenášitelné" (transferable), což zahrnuje:

  • Jednoduché datové typy (int, double, String, bool)
  • Seznamy a mapy obsahující přenášitelné typy
  • Některé specifické typy Flutteru, které lze serializovat

Objekty, které nejsou přenášitelné, budou zkopírovány a jejich následné změny v jednom izolátu se neprojeví v jiném.

Klíčové body:

  • Izoláty nemohou přímo přistupovat k paměti druhého.
  • Nemají přístup ke sdílenému stavu, včetně proměnných, tříd a widgetů hlavního izolátu (UI vlákna).
  • Komunikace mezi izoláty probíhá asynchronně.
  • Při práci s izoláty v Flutteru je důležité správně spravovat jejich životní cyklus (například použitím isolate.kill() podle potřeby).

Použití izolátů umožňuje provádět dlouhotrvající nebo náročné operace na pozadí, čímž se předchází zaseknutí uživatelského rozhraní a zajišťuje plynulost chodu aplikace.