Sobes.tech
Middle

Como criar dois threads em Dart no contexto do Flutter?

sobes.tech IA

Resposta da IA

Para criar threads de execução independentes em Dart/Flutter, usam-se Isolates. São tarefas separadas com seu próprio espaço de memória dedicado, o que previne problemas de acesso concorrente aos dados e bloqueios na thread principal (thread UI).

Existem várias formas de criar e interagir com os isolados:

  1. Isolate.spawn(): A forma de baixo nível para criar um isolamento. Requer passar uma função de nível superior ou um método estático de classe para executar no novo isolamento, bem como uma porta para trocar mensagens.

    import 'dart:isolate';
    
    // Função que é executada no isolamento
    void isolateEntry(SendPort sendPort) {
      // Executa alguma operação longa
      int result = 0;
      for (int i = 0; i < 1000000000; i++) {
        result += i;
      }
      // Envia o resultado de volta ao isolamento principal
      sendPort.send(result);
    }
    
    void main() async {
      ReceivePort receivePort = ReceivePort();
      // Cria um novo isolamento
      Isolate isolate = await Isolate.spawn(isolateEntry, receivePort.sendPort);
    
      // Ouve as mensagens do isolamento
      receivePort.listen((message) {
        print('Resultado do isolamento: $message');
        // Encerra o isolamento após receber a mensagem
        isolate.kill();
      });
    
      print('O thread principal continua a trabalhar...');
    }
    
  2. compute(): Função simplificada do pacote flutter/foundation, que usa Isolate.spawn() por baixo dos panos. Ideal para tarefas de fundo simples que não requerem interação complexa com o isolamento.

    import 'package:flutter/foundation.dart';
    
    // Função que é executada com compute
    int heavyComputation(int limit) {
      int result = 0;
      for (int i = 0; i < limit; i++) {
        result += i;
      }
      return result;
    }
    
    void main() async {
      print('Começando cálculos pesados em segundo plano...');
      // Executa a função em um isolamento separado
      int result = await compute(heavyComputation, 1000000000);
      print('Resultado dos cálculos: $result');
      print('O thread principal continua após os cálculos.');
    }
    
  3. Isolate.spawnUri(): Usado para executar código de um arquivo dart separado em um novo isolamento.

Interação entre isolados:

Os isolados trocam dados apenas por meio de envio de mensagens através de instâncias de SendPort e ReceivePort. Essas operações são atômicas, sem necessidade de bloqueios ou outros mecanismos de sincronização.

  • SendPort: Usado para enviar mensagens de um isolamento para outro.
  • ReceivePort: Usado para ouvir mensagens recebidas.

Ao transferir objetos entre isolados, eles devem ser "transferíveis", o que inclui:

  • Tipos de dados simples (int, double, String, bool)
  • Listas e mapas contendo tipos transferíveis
  • Alguns tipos específicos do Flutter que podem ser serializados

Objetos que não são transferíveis serão copiados, e suas alterações posteriores em um isolamento não serão refletidas no outro.

Pontos-chave:

  • Os isolados não podem acessar diretamente a memória um do outro.
  • Os isolados não têm acesso a estado compartilhado, incluindo variáveis, classes e widgets do isolamento principal (thread UI).
  • A comunicação entre isolados é assíncrona.
  • Ao trabalhar com isolados no Flutter, é importante gerenciar corretamente seu ciclo de vida (por exemplo, usar isolate.kill() se necessário).

O uso de isolados permite realizar operações longas ou intensivas em recursos em segundo plano, evitando que a interface do usuário congele e garantindo um funcionamento fluido do aplicativo.