Flutter开发-Isolate的创建与双向通信

4,263 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

介绍

众所周知,dart是单线程模型,没有Android中多线程的概念,但并不是说不存在异步,程序中异步操作是一定存在的,所以dart中提供了一种类似线程概念的东西-isolate,与线程不同的是,多个isolate之间是隔离的,内存是不共享的,所以是不存在并发问题的,也有人称之为isolate是像进程一样的线程。Android和Dart的虚拟机模型如下: WeChatb2a3bbce2e52f005218ebc5b896c05ce.png

isolate创建

在dart中,程序在启动时,执行main方法后会默认创建一个主isolate,那么我们该如何创建一个子isolate去执行异步任务呢? isolate中自己提供了静态方法spawn来创建一个isolate,该方法中需要传入两个必传参数:

  • entryPoint:指定要在生成的isolate中调用的初始函数。
  • message: entryPoint函数中要接收的参数,通常用于传递sendPort来进行isolate通信。
void main(){
  Isolate.spawn(entryPoint, "message");
}

void entryPoint(String message){
  print(message);
}

验证内存隔离

创建了子isolate之后,我们可以验证一下不同isolate之间是内存隔离的吗?如下声明一个全局num,在main进行赋值操作,分别在main isolate和子isolate中打印num的值,通过控制台日志可以发现子isolate中是获取不到num的值的。

int num;

void main(){
  num = 1;
  Isolate.spawn(entryPoint, "message");
  print("main num $num");
}

void entryPoint(String message){
  print("entryPoint num $num");
}

image.png

isolate通信

像Android中我们使用Handler等进行线程通信,那么看下dart中isolate是如何发送接收数据的。

ReceivePort receivePort = ReceivePort();
receivePort.listen((message) {
  print(message);
});
receivePort.sendPort.send("这是通信消息");
receivePort.sendPort.send(1);

通过创建一个接收器ReceivePort,使用它的发送器sendPort发送消息,并通过receivePort的listen方法监听来接收所发送的消息。需要注意的是,send中发送的数据是不限制数据类型的。

image.png

前面说了spawn中的message参数通常是传入main isolate的sendport,从而来进行不同isolate间的通信,也就是可以理解为,我把我的发送器给你,并准备一个接收器接收发送器发送的消息,当你需要发送消息的时候通过发送器把消息发送给我就可以了:

void main(){
  ReceivePort receivePort = ReceivePort();
  Isolate.spawn(entryPoint, receivePort.sendPort);
  receivePort.listen((message) {
    print(message);
  });
}

void entryPoint(SendPort sendPort){
  // 模拟耗时操作
  Future.delayed(Duration(seconds: 2),(){
    sendPort.send("子isolate里面给你发的消息");
  });
}

image.png 如图所示,main isolate在2s后接收到了来自子isolate中发送的消息。

双向通信

上边说我们可以把main isolate的发送器传递给子isolate来实现通信,那么同样也可以把子isolate的发送器传递给main isolate,这样就可以实现双向通信。由于send方法是不限制发送的数据类型的,因此可以利用此特性当接收到main isolate数据后首先把子isolate的发送器发送过去,之后在进行其他通信数据的发送:

void main(){
  ReceivePort receivePort = ReceivePort();
  Isolate.spawn(entryPoint, receivePort.sendPort);
  receivePort.listen((message) {
    if(message is SendPort){
      print("main接收到子isolate的发送器了");
      Future.delayed(Duration(seconds: 1),(){
        message.send("main给子isolate发送的消息");
      });
    }else{
      print(message);
    }
  });
}

void entryPoint(SendPort sendPort){
  ReceivePort receivePort = ReceivePort();
  receivePort.listen((message) {
    print("子isolate接收到main的消息了:$message");
  });
  sendPort.send(receivePort.sendPort);
  // 模拟耗时操作
  Future.delayed(Duration(seconds: 2),(){
    sendPort.send("子isolate里面给main发的消息");
  });
}

如下所示,main中首先接收到了来自子isolate发送过来的发送器,然后用此发送器给子isolate发送了消息,从而实现了两端双向通信的场景。

2021-09-27 18.07.14.gif

需要格外注意的是,当不再使用接收器时,一定要及时调用receivePort.close()

isolate的状态方法

暂停:

isolate.pause(isolate.pauseCapability);

恢复:

isolate.resume(isolate.pauseCapability);

结束:

isolate.kill(priority: Isolate.immediate);