Introducció

En Dart, els isolates són una manera de gestionar la concurrència. A diferència dels fils (threads) en altres llenguatges de programació, els isolates no comparteixen memòria. Cada isolate té el seu propi espai de memòria i comunicació amb altres isolates a través de missatges. Això fa que la programació concurrent sigui més segura i menys propensa a errors com les condicions de carrera.

Conceptes Clau

  • Isolate: Un procés lleuger amb el seu propi espai de memòria.
  • Port: Un canal de comunicació entre isolates.
  • SendPort: Un port que permet enviar missatges a un altre isolate.
  • ReceivePort: Un port que permet rebre missatges d'altres isolates.

Creació d'un Isolate

Per crear un isolate, utilitzem la funció Isolate.spawn. Aquesta funció pren com a arguments una funció i un missatge inicial que es passa a aquesta funció.

Exemple

import 'dart:isolate';

void isolateFunction(SendPort sendPort) {
  // Enviar un missatge de tornada al port principal
  sendPort.send('Hola des de l\'isolate!');
}

void main() async {
  // Crear un port de recepció
  ReceivePort receivePort = ReceivePort();

  // Crear un isolate
  await Isolate.spawn(isolateFunction, receivePort.sendPort);

  // Escoltar els missatges de l'isolate
  receivePort.listen((message) {
    print('Missatge rebut: $message');
    receivePort.close(); // Tancar el port després de rebre el missatge
  });
}

Explicació del Codi

  1. Importació del Mòdul: Importem el mòdul dart:isolate per utilitzar les funcionalitats d'isolate.
  2. Funció de l'Isolate: Definim una funció isolateFunction que rep un SendPort com a argument i envia un missatge a aquest port.
  3. Port de Recepció: Creem un ReceivePort en el main per rebre missatges de l'isolate.
  4. Creació de l'Isolate: Utilitzem Isolate.spawn per crear un nou isolate i passar-li el SendPort del ReceivePort.
  5. Escolta de Missatges: Utilitzem receivePort.listen per escoltar els missatges que arriben al ReceivePort i imprimir-los.

Comunicació entre Isolates

La comunicació entre isolates es fa mitjançant l'enviament de missatges a través de ports. Els missatges poden ser de qualsevol tipus que es pugui serialitzar, com ara cadenes, nombres, llistes i mapes.

Exemple Avançat

import 'dart:isolate';

void isolateFunction(SendPort sendPort) {
  // Crear un port de recepció dins de l'isolate
  ReceivePort isolateReceivePort = ReceivePort();

  // Enviar el port de recepció de l'isolate al port principal
  sendPort.send(isolateReceivePort.sendPort);

  // Escoltar els missatges del port de recepció de l'isolate
  isolateReceivePort.listen((message) {
    print('Missatge rebut a l\'isolate: $message');
    isolateReceivePort.close(); // Tancar el port després de rebre el missatge
  });
}

void main() async {
  // Crear un port de recepció
  ReceivePort receivePort = ReceivePort();

  // Crear un isolate
  await Isolate.spawn(isolateFunction, receivePort.sendPort);

  // Obtenir el port de recepció de l'isolate
  SendPort isolateSendPort = await receivePort.first;

  // Enviar un missatge a l'isolate
  isolateSendPort.send('Hola des del port principal!');

  // Tancar el port principal
  receivePort.close();
}

Explicació del Codi

  1. Port de Recepció dins de l'Isolate: Creem un ReceivePort dins de l'isolate per rebre missatges.
  2. Enviament del Port de Recepció: Enviem el SendPort del ReceivePort de l'isolate al port principal.
  3. Escolta de Missatges dins de l'Isolate: Utilitzem isolateReceivePort.listen per escoltar els missatges que arriben al ReceivePort de l'isolate.
  4. Enviament de Missatges des del Port Principal: Utilitzem el SendPort de l'isolate per enviar un missatge des del port principal.

Exercicis Pràctics

Exercici 1: Comunicació Bàsica

Crea un programa que utilitzi un isolate per calcular la suma de dos nombres i enviar el resultat al port principal.

Solució

import 'dart:isolate';

void sumFunction(List<dynamic> args) {
  int a = args[0];
  int b = args[1];
  SendPort sendPort = args[2];

  int sum = a + b;
  sendPort.send(sum);
}

void main() async {
  ReceivePort receivePort = ReceivePort();

  await Isolate.spawn(sumFunction, [5, 10, receivePort.sendPort]);

  int result = await receivePort.first;
  print('La suma és: $result');

  receivePort.close();
}

Exercici 2: Comunicació Bidireccional

Crea un programa on el port principal envia una llista de nombres a l'isolate, i l'isolate retorna la llista amb cada nombre multiplicat per 2.

Solució

import 'dart:isolate';

void doubleListFunction(SendPort sendPort) {
  ReceivePort isolateReceivePort = ReceivePort();
  sendPort.send(isolateReceivePort.sendPort);

  isolateReceivePort.listen((message) {
    List<int> numbers = message;
    List<int> doubledNumbers = numbers.map((n) => n * 2).toList();
    sendPort.send(doubledNumbers);
    isolateReceivePort.close();
  });
}

void main() async {
  ReceivePort receivePort = ReceivePort();

  await Isolate.spawn(doubleListFunction, receivePort.sendPort);

  SendPort isolateSendPort = await receivePort.first;

  List<int> numbers = [1, 2, 3, 4, 5];
  isolateSendPort.send(numbers);

  List<int> result = await receivePort.first;
  print('Llista multiplicada per 2: $result');

  receivePort.close();
}

Errors Comuns i Consells

  • No tancar els ports: Assegura't de tancar els ports de recepció (ReceivePort) després d'usar-los per evitar fuites de memòria.
  • Enviar objectes no serialitzables: Només envia objectes que es puguin serialitzar entre isolates.
  • No gestionar errors: Utilitza blocs try-catch per gestionar errors dins de les funcions dels isolates.

Conclusió

Els isolates són una eina poderosa per gestionar la concurrència en Dart. Permeten executar codi en paral·lel de manera segura, sense els problemes associats amb la memòria compartida. Amb la pràctica, podràs utilitzar els isolates per crear aplicacions més eficients i responsives.

© Copyright 2024. Tots els drets reservats