Flutter Isolate 适用场景小记
应用程序的流畅与否,主要取决于能否及时对用户操作作出响应。目前(2022-06-29)主流移动设备大都设计为以 60 fps
的速度刷新屏幕,在 60fps 的屏幕上,事件需在大约 16ms 内被执行。如果超过这个时间,画面将会出现卡顿。
核心奥义
- 非密集计算型耗时任务(CPU 负载较低),例如等待网络通信中的响应,应在 Main Isolate 上使用异步处理完成。
- 密集计算型耗时任务(CPU 负载较高),为不影响帧率,应启动新的 Isolate 执行并行处理。
- 使用其他 Isolate 运行任务的切换成本大约为 10~20ms 或更多(成本高于 Javascript 的轻量级线程)。
TODO:滥用 Isolate 会使代码复杂化,若只需执行某个耗时任务并获取其返回值,应使用 compute 函数。
TODO:任务耗时超过 16ms(在 60fps 终端的情况下),可判定为耗时任务。
TODO:为什么要使用 Isolate?为什么耗时任务会影响帧率?这两个问题在本文番外章节中有讲道。
TODO:常见的密集计算型耗时任务包括矩阵乘法、密码学相关(如签名、散列、密钥生成)、图像/音频/视频处理、序列化/反序列化、离线机器学习模型计算、压缩(如 zlib)、正则表达式等。
番外
Dart 是一种单线程语言,一次执行一个操作,一个接一个地执行。这意味着只要一个操作正在执行,就不能被其他 Dart 代码中断。 在幕后,每个 Isolate 都依靠自己的事件循环 (Event Loop
) 一个接一个的从队列中获取并执行任务。
一个 Isolate 中包含两个队列, 微任务队列 (MicroTask
) 和事件队列 (Event
),微任务队列中的任务优先执行,仅当微任务队列为空时,事件循环才会执行事件队列中的任务。
默认情况下所有任务都在 Main Isolate 上完成,包括 Flutter 的 UI 更新。如果遇到耗时任务,由于是单线程,包括 Flutter UI 更新在内的所有任务的都会被阻塞,这将导致帧率急剧下降。通常,只需将耗时任务放到新 Isolate 中即可避免这种问题的发生。
*TODO:多个 Isolate 之间不会共享内存,只能通过来回传递消息 (ReceivePort和SendPort) 实现相互通信。
TODO:Dart 单线程事件循环的执行模型与 Node.js 类似。
TODO:目前而言(2022-06-29),Flutter 平台通道通信仅受主隔离支持。这个主要隔离对应于应用程序启动时创建的隔离。
参考资料
- https://medium.com/flutter-jp/isolate-a3f6eab488b5
- https://medium.flutterdevs.com/threading-in-flutter-e5b84c7d8d31
- https://qiita.com/takyam/items/6ad155678c95bba4047f
- https://medium.com/flutter-community/understanding-isolates-in-flutter-5b26ec4ed627
- https://medium.com/flutter-community/flutter-dart-async-concurrency-demystify-1cc739aaae57