并发与 isolate
在 Flutter 中使用 Dart isolate 实现多线程。
所有 Dart 代码在 isolate 中运行,类似线程,但各有独立内存,不以任何方式共享状态,只能通过消息通信。默认情况下 Flutter 应用的所有工作都在单个 isolate——主 isolate——上完成。多数情况下该模型使编程更简单,且足够快,应用 UI 不会无响应。
但有时应用需要执行特别大的计算,可能导致「UI jank」(卡顿)。若因此出现 jank,可将计算移到辅助 isolate,让运行时环境与主 UI isolate 的工作并发执行,并利用多核设备。
每个 isolate 有独立内存和事件循环。事件循环按加入事件队列的顺序处理事件。在主 isolate 上,这些事件可以是处理 UI 点击、执行函数或在屏幕上绘制一帧等。 The following figure shows an example event queue with 3 events waiting to be processed.
为流畅渲染,Flutter 每秒向事件队列添加 60 次「paint frame」事件(60Hz 设备)。若这些事件未及时处理,应用会出现 UI jank,甚至更糟——完全无响应。
若某过程无法在帧间隔(两帧之间的时间)内完成,最好将工作卸载到另一 isolate,确保主 isolate 每秒产出 60 帧。在 Dart 中 spawn isolate 时,它可与主 isolate 并发处理工作而不阻塞主 isolate。
有关 isolate 与事件循环的更多说明,请参阅 Dart 文档的 concurrency page(并发页面)。
isolate 的常见用例
#何时应使用 isolate 只有一条硬性规则:大型计算导致 Flutter 应用出现 UI jank。当任何计算耗时超过 Flutter 的帧间隔时就会出现 jank。
Any process could take longer to complete, depending on the implementation and the input data, making it impossible to create an exhaustive list of when you need to consider using isolates.
此外,isolate 常用于以下场景:
Reading data from a local database
从本地数据库读取数据
Sending push notifications
发送推送通知
Parsing and decoding large data files
解析和解码大型数据文件
Processing or compressing photos, audio files, and video files
处理或压缩照片、音频和视频文件
Converting audio and video files
转换音频和视频文件
When you need asynchronous support while using FFI
使用 FFI 时需要异步支持
Applying filtering to complex lists or filesystems
对复杂列表或文件系统应用过滤
isolate 之间的消息传递
#
Dart 的 isolate 是 Actor model(Actor 模型)的实现,只能通过
Port objects 进行消息传递通信。消息在 isolate 之间「传递」时,通常从发送 isolate 复制到接收 isolate,因此传给 isolate 的值即使在该 isolate 上被修改,也不会改变原 isolate 上的值。
传给 isolate 时 objects that aren't copied when passed(不复制的对象)仅包括不可变对象,如 String 或不可修改的字节。传递不可变对象时,为提升性能会发送引用而非复制对象。因不可变对象无法更新,这有效保持了 Actor 模型行为。
例外是使用 Isolate.exit 发送消息时 isolate 退出:因发送 isolate 发送后不再存在,可将消息所有权从一个 isolate 转给另一个,确保只有一个 isolate 能访问该消息。
发送消息的两种底层原语是 SendPort.send(发送时复制可变消息)和 Isolate.exit(发送消息引用)。Isolate.run
和 compute 底层都使用 Isolate.exit。
短期 isolate
#
在 Flutter 中将过程移到 isolate 的最简单方式是使用 Isolate.run。该方法 spawn isolate,向 spawn 的 isolate 传递回调以开始计算,返回计算结果,计算完成后关闭 isolate。这一切与主 isolate 并发进行,不会阻塞主 isolate。
Isolate.run 需要一个参数:在新 isolate 上运行的回调函数。该回调的函数签名必须恰好有一个必需的无名参数。计算完成后,将回调的返回值返回主 isolate 并退出 spawn 的 isolate。
例如,以下代码从文件加载大型 JSON 并转换为自定义 Dart 对象。若 JSON 解码未卸载到新 isolate,该方法会使 UI 数秒无响应。
// Produces a list of 211,640 photo objects.
// (The JSON file is ~20MB.)
Future<List<Photo>> getPhotos() async {
final String jsonString = await rootBundle.loadString('assets/photos.json');
final List<Photo> photos = await Isolate.run<List<Photo>>(() {
final List<Object?> photoData = jsonDecode(jsonString) as List<Object?>;
return photoData.cast<Map<String, Object?>>().map(Photo.fromJson).toList();
});
return photos;
}
有关在后台使用 Isolate 解析 JSON 的完整 walkthrough,请参阅 this cookbook recipe(cookbook 食谱)。
有状态的长期 isolate
#
短期 isolate 使用方便,但 spawn 新 isolate 和在 isolate 间复制对象有性能开销。若反复用 Isolate.run 做相同计算,创建不立即退出的 isolate 可能性能更好。
为此可使用 Isolate.run 所封装的一些底层 isolate API:
-
Isolate.spawn()andIsolate.exit() -
ReceivePortandSendPort send()method
使用 Isolate.run 时,新 isolate 在向主 isolate 返回单条消息后立即关闭。有时你需要长期存活、可随时间互发多条消息的 isolate。在 Dart 中可用 Isolate API 和 Port 实现,这些长期 isolate 俗称
background workers(后台 worker)。
长期 isolate 适用于需要在应用生命周期内反复运行的特定过程,或在一段时间内运行并需向主 isolate 产生多个返回值的过程。
Or, you might use worker_manager to manage long-lived isolates.
ReceivePort 与 SendPort
#
Set up long-lived communication between isolates with two classes
(in addition to Isolate):
ReceivePort
and SendPort.
These ports are the only way isolates can communicate with each other.
Port 的行为类似 Stream:在一个 isolate 中创建 StreamController 或 Sink,在另一个 isolate 中设置监听器。类比中
StreamController 称为 SendPort,可用 send()「添加」消息;ReceivePort
是监听器,收到新消息时用消息作为参数调用提供的回调。
有关主 isolate 与 worker isolate 之间双向通信的深入说明,请参阅 Dart documentation(Dart 文档)中的示例。
在 isolate 中使用平台插件
#可在后台 isolate 中使用平台插件,使插件将繁重的平台相关计算卸载到不阻塞 UI 的 isolate。例如使用原生宿主 API 加密数据时,以前 marshaling data(编组数据)到宿主平台可能占用 UI 线程时间,现可在后台 isolate 完成。
以下片段展示在后台 isolate 中使用 shared_preferences 包的示例。
import 'dart:isolate';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
// Identify the root isolate to pass to the background isolate.
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
Future<void> _isolateMain(RootIsolateToken rootIsolateToken) async {
// Register the background isolate with the root isolate.
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
// You can now use the shared_preferences plugin.
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print(sharedPreferences.getBool('isDebug'));
}
Isolate 的限制
#
若你来自支持多线程的语言,可能预期 isolate 像线程一样工作,但事实并非如此。isolate 有独立全局字段,只能通过消息传递通信,确保可变对象仅在一个 isolate 中可访问,因此受限于自身内存访问。例如应用有全局可变变量
configuration,spawn 的 isolate 会复制为新全局字段;在 spawn 的 isolate 中修改该变量,主 isolate 中不变,即使将 configuration
作为消息传给新 isolate 亦然。这是 isolate 的设计行为,考虑使用 isolate 时需牢记。
Web 平台与 compute
#
包括 Flutter web 在内的 Dart Web 平台不支持 isolate。若 Flutter 应用面向 Web,可使用 compute 确保代码能编译。compute()
在 Web 上于主线程运行计算,在移动设备上 spawn 新线程。在移动和桌面平台上 await compute(fun, message) 等价于 await Isolate.run(() => fun(message))。
有关 Web 并发的更多信息,请参阅 dart.dev 的 concurrency documentation(并发文档)。
无法访问 rootBundle 或 dart:ui 方法
#
所有 UI 任务和 Flutter 本身都与主 isolate 绑定,因此无法在 spawn 的 isolate 中用 rootBundle 访问资源,也不能在 spawn 的 isolate 中执行 widget 或 UI 工作。
从宿主平台到 Flutter 的插件消息受限
#通过后台 isolate 平台通道,可在 isolate 中使用平台通道向宿主平台(如 Android 或 iOS)发送消息并接收响应,但无法接收来自宿主平台的主动消息。
例如,无法在后台 isolate 中设置长期 Firestore 监听器,因为 Firestore 通过平台通道向 Flutter 推送主动更新。但可在后台查询 Firestore 获取响应。
更多信息
#有关 isolate 的更多信息,请参阅以下资源:
-
If you're using many isolates, consider the IsolateNameServer class in Flutter, or the pub package that clones the functionality for Dart applications not using Flutter.
-
若使用多个 isolate,可考虑 Flutter 的 IsolateNameServer 类,或为非 Flutter 的 Dart 应用使用复制该功能的 pub 包。
-
Dart's Isolates are an implementation of the Actor model.
Dart 的 Isolate 是 Actor model 的实现。
-
isolate_agents is a package that abstracts Ports and make it easier to create long-lived isolates.
-
isolate_agents 是抽象 Port、便于创建长期 isolate 的包。
-
Read more about the
BackgroundIsolateBinaryMessengerAPI announcement. -
阅读
BackgroundIsolateBinaryMessengerAPI announcement(公告)的更多内容。
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-04。查看文档源码 或者 为本页面内容提出建议。