Sobes.tech
Middle

Comment créer deux threads en Dart dans le contexte de Flutter?

sobes.tech IA

Réponse de l'IA

Pour créer des threads d'exécution indépendants dans Dart/Flutter, on utilise des Isolates. Ce sont des tâches séparées avec leur propre espace mémoire dédié, ce qui empêche les problèmes d'accès concurrent aux données et de blocage du thread principal (thread UI).

Il existe plusieurs façons de créer et d'interagir avec les isolates :

  1. Isolate.spawn() : La méthode de bas niveau pour créer un isolate. Elle nécessite de passer une fonction de niveau supérieur ou une méthode statique de classe à exécuter dans le nouvel isolate, ainsi qu'un port pour échanger des messages.

    import 'dart:isolate';
    
    // Fonction exécutée dans l'isolate
    void isolateEntry(SendPort sendPort) {
      // Effectuer une opération longue
      int result = 0;
      for (int i = 0; i < 1000000000; i++) {
        result += i;
      }
      // Envoyer le résultat de retour à l'isolate principal
      sendPort.send(result);
    }
    
    void main() async {
      ReceivePort receivePort = ReceivePort();
      // Créer un nouvel isolate
      Isolate isolate = await Isolate.spawn(isolateEntry, receivePort.sendPort);
    
      // Écouter les messages de l'isolate
      receivePort.listen((message) {
        print('Résultat de l’isolate : $message');
        // Terminer l'isolate après réception
        isolate.kill();
      });
    
      print('Le thread principal continue de fonctionner...');
    }
    
  2. compute() : Fonction simplifiée du package flutter/foundation, qui utilise Isolate.spawn() en interne. Idéal pour des tâches de fond simples ne nécessitant pas d'interaction complexe avec l'isolate.

    import 'package:flutter/foundation.dart';
    
    // Fonction exécutée avec compute
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      print('Démarrage de calculs lourds en arrière-plan...');
      // Exécuter la fonction dans un isolate séparé
      int result = await compute(heavyComputation, 1000000000);
      print('Résultat des calculs : $result');
      print('Le thread principal continue après les calculs.');
    }
    
  3. Isolate.spawnUri() : Utilisé pour lancer du code à partir d'un fichier dart séparé dans un nouvel isolate.

Interaction entre isolates :

Les isolates échangent des données uniquement par envoi de messages via des instances de SendPort et ReceivePort. Ces opérations sont atomiques, sans nécessiter de verrouillages ou autres mécanismes de synchronisation.

  • SendPort : Utilisé pour envoyer des messages d'un isolate à un autre.
  • ReceivePort : Utilisé pour écouter les messages entrants.

Lors du transfert d'objets entre isolates, ils doivent être "transférables", ce qui inclut :

  • Types de données simples (int, double, String, bool)
  • Listes et cartes contenant des types transférables
  • Certains types spécifiques à Flutter pouvant être sérialisés

Les objets non transférables seront copiés, et leurs modifications ultérieures dans un isolate ne seront pas reflétées dans l'autre.

Points clés :

  • Les isolates ne peuvent pas accéder directement à la mémoire de l'autre.
  • Les isolates n'ont pas accès à un état partagé, y compris variables, classes et widgets de l'isolate principal (thread UI).
  • La communication entre isolates est asynchrone.
  • Lors de l'utilisation d'isolates dans Flutter, il est important de gérer correctement leur cycle de vie (par exemple, utiliser isolate.kill() si nécessaire).

L'utilisation d'isolates permet d'effectuer des opérations longues ou intensives en ressources en arrière-plan, évitant que l'interface utilisateur se bloque et assurant un fonctionnement fluide de l'application.