Dart – Isolates

Isolates operate on its own threads, where async operations operate on the main thread.

Bidirectional communication

Bidirectional communication between the main thread and isolate1/isolate2 and between isolate2 and isolate1 (with the help of the main thread).

import 'dart:io';
import 'dart:isolate';

main(List<String> args) async {
  ReceivePort isolate1ToMainStream = new ReceivePort();
  ReceivePort isolate2ToMainStream = new ReceivePort();

  SendPort? mainToIsolateStream1;

  isolate1ToMainStream.listen((input) {
    Map data = input as Map;

    if (data['type'] == 'sendPort') {
      mainToIsolateStream1 = data['value'];
      mainToIsolateStream1!.send('Message from Main');
    } else {
      print('Message recieved in Main: ${data['value']}');
    }
  });

  isolate2ToMainStream.listen((input) {
    Map data = input as Map;

    switch (data['type']) {
      case 'sendPort':
        SendPort mainToIsolateStream = data['value'];
        mainToIsolateStream.send('Message from Main');
        break;
      case 'redirect':
        if (mainToIsolateStream1 != null) {
          mainToIsolateStream1!.send(data['value']);
        }

        break;
      case 'message':
        print('Message recieved in Main: ${data['value']}');
        break;
      default:
        print('Undefined');
    }
  });

  Isolate isolate1 =
      await Isolate.spawn(myIsolate1, isolate1ToMainStream.sendPort);

  Isolate isolate2 =
      await Isolate.spawn(myIsolate2, isolate2ToMainStream.sendPort);

  await stdin.first;

  isolate1.kill(priority: Isolate.immediate);
  isolate2.kill(priority: Isolate.immediate);

  isolate1ToMainStream.close();
  isolate2ToMainStream.close();
}

//Isolate1
void myIsolate1(SendPort isolateToMainStream) {
  ReceivePort mainToIsolateStream = ReceivePort();

  mainToIsolateStream.listen((data) {
    print('Message recieved in Isolate1: $data');
  });

  isolateToMainStream.send({
    'type': 'sendPort',
    'value': mainToIsolateStream.sendPort,
  });

  isolateToMainStream.send({
    'type': 'message',
    'value': 'Message from Isolate1',
  });
}

// Isolate2
void myIsolate2(SendPort isolateToMainStream) {
  ReceivePort mainToIsolateStream = ReceivePort();

  mainToIsolateStream.listen((data) {
    print('Message recieved in Isolate2: $data');
  });

  isolateToMainStream.send({
    'type': 'sendPort',
    'value': mainToIsolateStream.sendPort,
  });

  isolateToMainStream.send({
    'type': 'message',
    'value': 'Message from Isolate2',
  });

  isolateToMainStream.send({
    'type': 'redirect',
    'value': 'Message from Isolate2 to isolate1',
  });
}

Another example where the messages when isolates are spawn are a class.

import 'dart:io';
import 'dart:isolate';
import 'dart:math';

//Isolate operate on its own thread,
//where async operations operate on the main thread.

List<Isolate> isolates;

class Arguments {
  String name;
  String message;
  SendPort sendPort;

  Arguments({this.name, this.message, this.sendPort});
}

///Crate isolates
void start() async {
  isolates = new List();
  ReceivePort receivePort = ReceivePort();

  Arguments arguments1 = Arguments(name: 'Isolate number 1', message: '100000000', sendPort: receivePort.sendPort);
  Arguments arguments2 = Arguments(name: 'Isolate number 2', message: '10000000', sendPort: receivePort.sendPort);

  //Create two isolates
  isolates.add(await Isolate.spawn(calculation, arguments1));
  isolates.add(await Isolate.spawn(calculation, arguments2));
  //isolates.add(await Isolate.spawn(someOtherThing, someOtherArguments));

  //Listen for messages from the isolates
  receivePort.listen((data) {
    print('Data: $data');
  });
}

//Just do some heavy calculations
void calculation(Arguments arguments) {
  int _total = int.parse(arguments.message);
  for (int _a = 0; _a < _total; _a++) {
    double _tmp = sin(_a) * cos(_a);

    if (_a % (_total / 10) == 0) {
      String msg = '${arguments.name} status $_a';
      arguments.sendPort.send(msg);
    }
  }
}

//Kill the isolates
void stop() {
  for (Isolate i in isolates) {
    if (i != null) {
      i.kill(priority: Isolate.immediate);
      i = null;
      print('Killed');
    }
  }
}

void main() async {
  await start();

  print('Press enter to exit');
  await stdin.first;

  stop();
  exit(0);
}