文章

Dart Isolate 隔离机制深度解析:多线程不会用?Dart 直接不让你用共享内存

Isolate 是 Dart 的并发模型:讲清基于 Actor 模型的消息传递、无共享内存、与主 Isolate 的通信(ReceivePort/SendPort)。 掌握后能说清「为什么 Dart 没有传统多线程共享内存问题」。

Dart Isolate 隔离机制深度解析:多线程不会用?Dart 直接不让你用共享内存

一句话概括

Isolate 是 Dart 的并发方案——每个 Isolate 有独立堆内存和事件循环,之间通过消息传递通信,从根本上消灭了数据竞争和死锁。代价是不能共享内存,好处是你永远不用写锁。

核心知识点

1. Isolate 的生命周期:spawn → 通信 → kill

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'dart:isolate';

void heavyTask(SendPort sendPort) {
  // 在独立 Isolate 中运行,不阻塞 UI
  int sum = 0;
  for (int i = 0; i < 100000000; i++) sum += i;
  sendPort.send(sum); // 通过端口发回结果
}

void main() async {
  final receivePort = ReceivePort();
  await Isolate.spawn(heavyTask, receivePort.sendPort);

  receivePort.listen((message) {
    print('计算结果: $message'); // 主 Isolate 收到结果
    receivePort.close();
  });
}

关键步骤:Isolate.spawn(函数, 初始参数) 创建 → SendPort 发消息 → ReceivePort 收消息 → 用完关闭 port。

2. Flutter 中的便捷封装:compute()

1
2
3
4
5
6
7
8
9
10
import 'package:flutter/foundation.dart';

int fibonacci(int n) => n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);

void main() {
  // compute 自动 spawn + 收发 + kill
  compute(fibonacci, 42).then((result) {
    print('F(42) = $result');
  });
}

compute() 只适合单次任务:传一个函数+一个参数,拿到结果后 Isolate 自动销毁。参数和返回值必须是可序列化的(基本类型或简单对象)。

3. 双向通信:多个 SendPort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void worker(SendPort mainPort) {
  final workerPort = ReceivePort();
  mainPort.send(workerPort.sendPort); // 先把自己的 sendPort 发回去

  workerPort.listen((msg) {
    final result = '处理: $msg';
    mainPort.send(result);
  });
}

void main() async {
  final mainPort = ReceivePort();
  await Isolate.spawn(worker, mainPort.sendPort);

  // 等 worker 发来它的 sendPort
  late SendPort toWorker;
  mainPort.listen((msg) {
    if (msg is SendPort) {
      toWorker = msg;
      toWorker.send('开始工作');
    } else {
      print('收到回应: $msg');
    }
  });
}

双向通信 = 互相邮寄 SendPort。这是 Isolate 之间持续通信的标准模式,类似 TCP 的握手建立连接。

4. 什么情况下必须用 Isolate?

1
2
3
4
5
// ❌ 错误:耗时的 JSON 解析会卡 UI
final json = jsonDecode(hugeString); // 可能 500ms+,UI 卡死

// ✅ 正确:放到 Isolate 中
final json = await compute(jsonDecode, hugeString);

判断标准:任何同步耗时 > 16ms 的操作都应该考虑 Isolate。包括大 JSON 解析、图片编解码、加密解密、复杂数学计算。

5. Isolate 的局限性

1
2
3
4
5
6
7
// ❌ Isolate 之间不能共享对象
// ❌ 不能访问其他 Isolate 的内存
// ❌ 消息传递只能传可序列化数据(基本类型、List、Map、SendPort)
// ❌ 不能传递闭包、Stream、File 对象

// ✅ Isolate 之间可以传递 SendPort,建立"通道"
// ✅ 可以用 IsolateNameServer 给 Isolate 起名字互相查找

其实你每天都在用

  • compute() 处理大 JSON:jsonDecode 500ms 的响应在 Isolate 里跑,UI 不卡
  • 图片缓存库 cached_network_image:内部用 Isolate 做图片解码
  • path_provider 的文件操作:不是 Isolate 但底层 IO 是异步的,不会卡主线程
  • 加密库 encrypt 的大文件处理:对称加密/解密放 Isolate,避免 UI 丢帧
  • Flutter 引擎本身:GPU、IO、Platform 线程就是独立 Isolate

常见误解(FAQ)

❌ 误区:「Isolate 就是 Dart 的线程」

不是。Isolate 有独立堆内存,线程共享堆内存。线程之间的数据竞争和死锁在 Isolate 中不可能发生——因为你根本没机会访问另一个 Isolate 的内存。这是一个设计上的强约束,不是实现缺陷。

❌ 误区:「任何异步操作都应该用 Isolate」

Isolate 有创建开销(~100ms)和内存开销(~5MB)。网络请求这种 IO 等待操作用 async/await 就够,只有 CPU 密集型计算才需要用 Isolate。

❌ 误区:「多个 Isolate 能自动利用多核 CPU」

能。每个 Isolate 可以运行在不同的 CPU 核心上,Dart VM 会自动调度。这是”真正的并行”,不依赖 GIL。

❌ 误区:「Isolate 之间传数据是通过复制」

是的,消息传递时数据会被深拷贝到目标 Isolate 的堆。大数据的传参要留意拷贝成本。TransferableTypedData 可以零拷贝传输大块二进制数据。


一句话总结

Isolate = 独立内存 + 独立事件循环 + 消息传递,让你在 Dart 里做真正的并行计算,还不用担心锁和竞态——这是 Dart 并发模型最优雅的地方。

本文由作者按照 CC BY 4.0 进行授权