Dart 多线程:Isolate

Dart 被设计成为单线程、异步的语言。 尤其在 Flutter 当中,大部分的 Dart 代码都执行在一个线程,应该叫一个 Isolate 里面。

单线程带来了很多好处,不用考虑数据被同时访问,不用加锁,也就有没有了死锁;也给内存的垃圾回收带来了方便。

Isolate 对象 Link to heading

  • Isolate 代表了 Dart 执行的上下文,所有的 Dart 代码都运行在其中。
  • Dart 代码只能访问到当前 Isolate 的类和变量,不同 Isolate 之间内存是隔离的,只能通过 Ports 进行通讯。
  • 每一个 Isolate 都有独立的消息循环和垃圾回收。
  • Isolate 可以被其他的 Isolate 控制,比如暂停,终止等。 这些操作都比较危险,可以做访问控制。
  • Isolate 对象不能在多个 Isolate 之间进行传递,但是 SendPort 对象和 Capability 对象可以传递。

Port 对象 (ReceivePort, RawRecivePort, SendPort) Link to heading

  • Port 是多个 Isolate 之间通讯的管道,是单向的。
  • ReceivePort 和 SendPort 是成对使用的,SendPort 是由 ReceivePort 创建的,通过 ReceivePort 对象或 RawRecivePort 对象的 sendPort 成员获取。
  • ReceivePort 是一个 Stream, 可以直接监听。
  • RawReceivePort 比较底层,需要设置处理函数 handler 来接收消息; handler 的调用没有 Zone 的概念,所以永远在 Zone.root 里面。

Capability 对象 Link to heading

  • Capability 对象是可以在多个 Isolate 之间传递的对象,因为内存是隔离的,理论上不同的 Isolate 里面是不可能有相同对象的,Capablility 对象是专门实现的一种特殊对象,在不同 Isolate 传递时,还能保持相等。
  • Capability 对象可以作为凭据在 Isolate 间对 Isolate 的操作做访问控制。

代码示例 Link to heading

import 'dart:async';
import 'dart:isolate';

main() async {
  var receivePort = new ReceivePort();
  await Isolate.spawn(echo, receivePort.sendPort);

  // The 'echo' isolate sends it's SendPort as the first message
  var sendPort = await receivePort.first;

  var msg = await sendReceive(sendPort, "foo");
  print('received $msg');
  msg = await sendReceive(sendPort, "bar");
  print('received $msg');
}

// the entry point for the isolate
echo(SendPort sendPort) async {
  // Open the ReceivePort for incoming messages.
  var port = new ReceivePort();

  // Notify any other isolates what port this isolate listens to.
  sendPort.send(port.sendPort);

  await for (var msg in port) {
    var data = msg[0];
    SendPort replyTo = msg[1];
    replyTo.send(data);
    if (data == "bar") port.close();
  }
}

/// sends a message on a port, receives the response,
/// and returns the message
Future sendReceive(SendPort port, msg) {
  ReceivePort response = new ReceivePort();
  port.send([msg, response.sendPort]);
  return response.first;
}