有没有一种感觉,就我自己而言,Flutter 项目开发了好几个了,但是对这个 isolates 印象依旧很陌生,日常开发中好像很少见到它身影或者用到它,但真实情况是这样的吗?今天就来聊一聊它。
Flutter 中的 isolates 是什么?
在 Flutter 中,Isolates(隔离区)是一种轻量级的并发执行单元,用于在单个进程中执行代码。与线程有点儿像,但是每个 Isolates 之间又是完全独立的,彼此之间没有共享内存。每个 Isolates 都有自己的内存堆,可以独立执行代码,处理任务,而不会受到其他 Isolates 的影响。它们之间只能通过消息传递进行通信。
我们可以通过 Isolate.current 来查看当前正在执行的 Isolate

Flutter 中有哪些 isolates ?
在 Flutter 中,主要有两种类型的 Isolates
-
Main Isolates: 也称为UI线程或UI Isolate,启动应用程序时,Dart VM会自动创建一个Isolate实例并在其上运行您的“主”代码,相当于是Flutter应用程序的主线程,负责处理用户界面和用户交互。在Main Isolate中执行的代码通常包括构建UI、处理用户输入等任务。打印Isolate.current.debugName可以看到:

-
Background Isolates:运行在后台,可以用于执行耗时操作,如网络请求、长的JSON解析、文件读取、计算密集型任务等。通过后台Background Isolates,可以避免在主Isolate中执行长时间运行的任务,从而保持应用程序的响应性。
默认情况下,Flutter 应用程序会在 Main Isolates 上完成所有工作,切处理速度很快,不会出现 UI 卡顿,但执行异常大的计算的时候,会出现 UI 卡顿,和原生开发一样,在子线程执行耗时操作,在 Flutter 中就需要将耗时操作放在辅助的 Isolates 中,也就是 Background Isolates。
事件循环与页面卡顿
每个 Isolates 都有自己的内存和事件循环,事件循环是按照事件添加到事件队列的顺序来处理事件。在 Main Isolates 中,这些事件可以是用户的点击事件、函数执行和绘制视图到屏幕上等等。 为了更平滑的渲染视图,Flutter 底层会以 60次/每秒 向事件队列添加“绘制帧”事件(对于 60Hz 设备)。 如果这些事件没有及时处理,就会出现 UI 卡顿或者没有响应。

如果某个事件无法在两帧之间的时间(帧间隙)内完成时,最好将这个事件的执行放在其它的 Isolates,用来确保 Main Isolates 每秒可以生成 60 帧。
Isolates 之间通信
创建一个新的 isolate 有两种方式,构造方法 Isolate(...) 和 静态方法 Isolate.spaw。下面例子创建一个新的 isolate 每隔一秒向 Main Isolates 发送当前时间戳。
Future<void> main() async {
if (kDebugMode) {
print("Isolate.current = ${Isolate.current.debugName}");
}
Isolate timerIsolate;
final mainControlPort = ReceivePort()..listen((message) {
print("sending back to MainIsolate: $message");
});
timerIsolate = await Isolate.spawn(
timerTick,
mainControlPort.sendPort,
debugName: "TimerIsolate",
);
runApp(App());
}
void timerTick(SendPort mainPort) async {
print("${Isolate.current.debugName} started");
Timer.periodic(const Duration(seconds: 1), (timer) {
final ts = DateTime.now().toIso8601String();
mainPort.send(ts);
});
}
执行结果的 log:
Performing hot restart...
Syncing files to device iphone...
Restarted application in 886ms.
flutter: Isolate.current = main
flutter: TimerIsolate started
flutter: sending back to MainIsolate: 2024-03-29T16:49:39.598746
flutter: sending back to MainIsolate: 2024-03-29T16:49:40.594376
flutter: sending back to MainIsolate: 2024-03-29T16:49:41.594763
flutter: sending back to MainIsolate: 2024-03-29T16:49:42.594927
flutter: sending back to MainIsolate: 2024-03-29T16:49:43.593959
flutter: sending back to MainIsolate: 2024-03-29T16:49:44.594098
flutter: sending back to MainIsolate: 2024-03-29T16:49:45.594441
flutter: sending back to MainIsolate: 2024-03-29T16:49:46.594051
flutter: sending back to MainIsolate: 2024-03-29T16:49:47.594242
Application finished.

实际上,Flutter 中Isolates 就是 Actor model 实现,Isolates 之间只能通过消息传递来相互通信,而且消息是通过复制的方式从发送 Isolate 传递到接收的 Isolate,也就是说,当这个数据消息在接收的 Isolate 上发生变化的时候,发送 Isolate 的原数据不受影响。
SendPort.send: 发送时生成可变消息的副本
传递的消息是不可变对象时 immutable objects,如不可变的字符串,会发送对该对象的引用,而不是复制的对象,这样做也是为了获得更好的性能。不可变对象无法更新,也符合Actor model 的实现。
Isolate.exit:发送对消息的引用
特殊情况就是当发送方Isolate 在发送完消息后就销毁了,会将消息的所有权传递给接收的 Isolate,以确保只有一个 Isolate 可以访问该消息。
根据这些特性,所以我们在使用 Isolate 要注意这一点,当你在 Isolates 传递一个全局可变变量时,该全局变量会在其它 Isolate 复制一份,而在主隔离中却保持不变。
还有一点,目前 Web 平台还不支持 Isolate 。
本篇文章就到这里,感谢您的阅读!
