Dart【07】Isolate并发编程

272 阅读4分钟

Isolate简介

什么是Isolate

isolate就像是机器上的一个小空间,带有自己的私有内存块和一个运行事件循环的线程。

两个Isolate,每个Isolate都有自己的内存和执行线程。

Isolate的作用

如果要执行的计算量很大,在主Isolate中运行可能会导致丢帧,则可以使用Isolate.spawn()Flutter的compute()函数。这两个函数都创建了一个单独的Isolate程序进行越暖,使主Isolate程序可以自由地进行其他操作(例如:Flutter中的重建和渲染小部件树)。

PS:阅读源码可知Fluttercompute()函数是对Isolate.spawn()的封装

多个Isolate如何一起工作

Isolate通过互相传递消息一起工作。一个Isolate将消息发送到另一个,接收消息的Isolate使用其事件循环处理该消息。

创建Isolate

使用Isolate类下的静态方法spwan来创建Isolate

Isolate.spawn方法

下面来介绍一下Isolate.spawn方法的参数

  • 必填参数entryPoint: 它是Isolate程序中调用的初始函数。该函数必须是顶级函数或静态方法,且该函数必须有一个和isolate的参数message同种类型的参数。

  • 必填参数message: 它的作用就是为entryPoint函数提供参数,值得一提的是message通常包含一个SendPort,以便不同的isolate进行通信。

  • 可选参数paused: 如果paused参数设置为trueIsolate将以暂停状态启动。暂停状态启动就像是调用entryPoint函数之前,调用了isolate.pause(isolate. pausecapability),我们能直接看到的效果就是如果Isolate以暂停状态启动,entryPoint函数并不会执行。如果想要恢复Isolate程序,调用 isolate.resume(isolate. pausecapability)

    值得一提的是:Isolate暂停功能通过暂停事件队列来工作。当前正在执行的事件将完成,直到您恢复Isolate之前,不会再处理其他事件。暂停不会影响正在运行的代码。

  • 可选参数errorsAreFatal: 设置未捕获的错误是否将终止Isolate。如果错误是致命的,则任何未捕获的错误都会终止Isolate事件循环并关闭隔离。

Isolate.spawn方法的返回值为Future,如果创建Isolate成功完成,FuturevalueIsolate类型。

创建Isolate的例子

创建一个最简单的Isolate

import 'dart:isolate';
​
void main() {
  Isolate.spawn(isolateFunc, "message");
}
​
isolateFunc(message) {
  print(message);
}

输出如下

message

当启动应用程序时,新的Isolate被创建,isolateFunc方法在新创建的Isolate中执行。

到了这一步已经可以满足大部分使用Isolate的场景了,即把会导致UI卡顿的任务用额外的Isolate来执行。

获取Isolate对象的两种方式

Isolate.spwan方法返回的是Future,我们该如何做才能拿到Isolate对象进行操作呢?

  1. 在Future的then回调中进行赋值操作

    Isolate isolate;
    ​
    void main() {
      Isolate.spawn(isolateFunc, "message").then((value) {
        isolate = value;
      });
    }
    ​
    isolateFunc(message) {
    }
    
  2. 使用async,和await简化第一种方法

    Isolate isolate;
    ​
    void main() async {
      isolate = await Isolate.spawn(isolateFunc, "message");
    }
    ​
    isolateFunc(message) {}
    

Isolate常用方法

kill

请求Isolate终止自身。该函数的priority参数指定何时执行此操作。

priority参数必须是0(immediate)或 1(beforeNextEvent)。根据优先级在不同的时间执行关闭操作:

  • immediateIsolate将尽快关闭。控制消息是按顺序处理的,因此来自此Isolate的所有先前发送的事件都将被处理。如果系统有办法在更早的时间彻底关闭,甚至在执行另一个事件期间,也可能会更早发生。
  • beforeNextEvent:计划在当前事件以及所有已调度的事件完成后关闭。

示例:

  isolate.kill();

pause

暂停isolate

isolate收到暂停命令时,它将停止处理来自事件循环队列的事件,已经在事件循环队列中的事件还会继续执行。

示例:

isolate.pause(isolate.pauseCapability);

resume

结束暂停

示例:

isolate.resume(isolate.pauseCapability);

Isolate的通讯

isolate单向通信

Isolate之间的通讯主要是通过ReceivePort类实现的,我们通过一段代码示例来讲解Isolate之间的通讯。

// 导入isolate包
import 'dart:isolate';
​
void main() async {
    // 创建一个ReceivePort用于接收消息
    var recv = ReceivePort();
​
    // 创建一个Isolate,泛型参数为SendPort,入口函数为subTask
    // subTask入口函数的参数为SendPort类型,因此spawn第二个参数,传入recv的sendPort对象。
  Isolate.spawn<SendPort>(subTask, recv.sendPort);
        
    // 使用await等待recv的第一条消息
    var result = await recv.first;
​
    print("recv: $result");
}
​
// Isolate入口函数定义,接收一个SendPort对象作为参数
void subTask(SendPort port) {
    // 使用SendPort发送一条字符串消息
  port.send("subTask Result.");
}

ReceivePortDart中的Stream

上面的例子实现了新创建的Isolate发送消息到main程序。

阅读源码ReceivePort是通过StreamController创建的Stream

双向通信

//@dart = 2.16// Example:import 'dart:async';
import 'dart:isolate';
​
void main() async {
  var isolateReceivePort = ReceivePort();
  var isolate = await Isolate.spawn(runIsolate, isolateReceivePort.sendPort);
  late SendPort sendPort;
  var completer = Completer();
  await isolateReceivePort.listen((msg) {
    if (msg is SendPort) {
      completer.complete(msg);
    } else {
      print('Received in isolate: $msg');
    }
  });
  sendPort = await completer.future;
  sendPort.send("main");
}
//次级isolate创建ReceivePort,监听从其他isolate传来的数据,
// 通过初始化时接到的sendPort将自己创建的receivePort的sendPort发送出去
//主isolate接到sendPort,用这个端口给次级isolate发消息
void runIsolate(SendPort sendPort) {
  var receivePort = ReceivePort();
  receivePort.listen((msg) {
    print('Received in main: $msg');
  });
  sendPort.send('Message from isolate');
  sendPort.send(receivePort.sendPort);
}
​