现在位置: 首页 > Dart 教程 > 正文

Dart 并发与 Isolate

并发是指同时处理多个任务的能力。

Dart 的并发模型不同于传统的多线程,它使用 Isolate(隔离区)来实现并行计算。

本章介绍 Dart 的并发模型、Isolate 的概念与使用以及消息传递机制。


Dart 并发模型

大多数编程语言使用共享内存的多线程模型,多个线程共享同一个内存空间。

这种模型的缺点是容易产生竞态条件(race condition)和死锁(deadlock)。

Dart 采用了不同的策略:每个 Isolate 拥有自己独立的内存堆,Isolate 之间不共享内存。

它们通过消息传递来通信,这从根本上避免了数据竞争问题。

Dart 的并发模型有三个层次:

层次机制适用场景
事件循环单线程异步(Event Loop)I/O 操作、定时器、用户交互
Isolate独立内存 + 消息传递CPU 密集型计算
Future/Stream异步编程语法大多数日常开发场景

大多数 Dart 程序的并发需求都可以通过 async/await 和 Future/Stream 来满足。Isolate 只在你需要进行大量 CPU 密集型计算时才有必要使用。过早引入 Isolate 会让代码变复杂,收益却不大。


Isolate 概念与使用

Isolate 是 Dart 的并发单元,每个 Isolate 有自己独立的内存和事件循环。

主程序本身就在一个 Isolate(主 Isolate)中运行。

使用 Isolate.spawn 创建新 Isolate

实例

import 'dart:isolate';

// 这个函数会在新的 Isolate 中运行
// SendPort 用于向主 Isolate 发送消息
void heavyComputation(SendPort sendPort) {
  print('新 Isolate 开始计算...');

  // 模拟 CPU 密集型计算
  int sum = 0;
  for (int i = 1; i <= 10000000; i++) {
    sum += i;
  }

  // 将计算结果发送回主 Isolate
  sendPort.send(sum);

  print('新 Isolate 计算完成,结果已发送');
}

Future<void> main() async {
  print('主 Isolate 启动');

  // 创建一个 ReceivePort 用于接收消息
  var receivePort = ReceivePort();

  // spawn 创建新的 Isolate
  await Isolate.spawn(heavyComputation, receivePort.sendPort);

  print('主 Isolate 在等待结果的同时可以做其他事情...');

  // 等待新 Isolate 的计算结果
  var result = await receivePort.first;
  print('RUNOOB 计算结果: 1 到 10000000 的和 = $result');

  receivePort.close();
  print('主 Isolate 结束');
}
主 Isolate 启动
主 Isolate 在等待结果的同时可以做其他事情...
新 Isolate 开始计算...
新 Isolate 计算完成,结果已发送
RUNOOB 计算结果: 1 到 10000000 的和 = 50000005000000
主 Isolate 结束

对比:有无 Isolate 的性能差异

实例

// 在主 Isolate 中执行 CPU 密集任务(会阻塞)
int fibonacci(int n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

void main() {
  // 在主 Isolate 中计算——会阻塞所有其他操作
  var startTime = DateTime.now();

  // 注意:如果 n 太大,这个计算会非常耗时
  // 实际开发中应该把这种计算放到单独的 Isolate 中
  var result = fibonacci(40);

  var elapsed = DateTime.now().difference(startTime);
  print('RUNOOB 斐波那契(40) = $result');
  print('耗时: ${elapsed.inMilliseconds}ms');
  print('(注意:在计算期间,主 Isolate 无法处理其他任务)');
}

在 Flutter 应用中,如果在主 Isolate 中执行耗时的同步计算,会导致 UI 冻结。解决方案就是将计算任务放到单独的 Isolate 中,计算完成后通过消息将结果传回并更新 UI。


消息传递机制

Isolate 之间通过 SendPort 和 ReceivePort 进行消息传递。

消息必须是可序列化的(基本类型、String、List、Map 等),不能传递函数或闭包。

双向通信

实例

import 'dart:isolate';

// 在新 Isolate 中运行的工作函数
void workerIsolate(SendPort mainSendPort) {
  // 创建自己的 ReceivePort 来接收主 Isolate 的消息
  var workerReceivePort = ReceivePort();

  // 先把 worker 的 SendPort 发给主 Isolate,建立双向通道
  mainSendPort.send(workerReceivePort.sendPort);

  print('Worker Isolate: 等待任务...');

  // 监听主 Isolate 发来的任务
  workerReceivePort.listen((message) {
    if (message is List<int>) {
      // 收到任务:对列表中的每个数求平方
      print('Worker Isolate: 收到数据 $message');
      var result = message.map((n) => n * n).toList();

      // 通过 mainSendPort 发回结果
      mainSendPort.send(result);
    } else if (message == 'exit') {
      print('Worker Isolate: 收到退出信号,关闭');
      workerReceivePort.close();
      mainSendPort.send('goodbye');
    }
  });
}

Future<void> main() async {
  print('主 Isolate: 启动');

  // 创建主接收端口
  var mainReceivePort = ReceivePort();

  // 启动 Worker Isolate
  await Isolate.spawn(workerIsolate, mainReceivePort.sendPort);

  // 等待 Worker 发来它的 SendPort(建立双向通信)
  SendPort? workerSendPort;
  await for (var msg in mainReceivePort) {
    if (msg is SendPort) {
      workerSendPort = msg;
      print('主 Isolate: 已建立与 Worker 的双向通信');
      break;
    }
  }

  // 通过 Worker 的 SendPort 发送任务
  workerSendPort!.send([1, 2, 3, 4, 5]);
  workerSendPort.send([10, 20, 30]);

  // 接收 Worker 的计算结果
  int responseCount = 0;
  await for (var msg in mainReceivePort) {
    if (msg is List<int>) {
      print('主 Isolate: 收到结果 $msg');
      responseCount++;
      if (responseCount == 2) break;
    }
  }

  // 发送退出信号
  workerSendPort.send('exit');
  await for (var msg in mainReceivePort) {
    if (msg == 'goodbye') {
      print('主 Isolate: Worker 已退出');
      break;
    }
  }

  mainReceivePort.close();
  print('RUNOOB 双向通信演示结束');
}
主 Isolate: 启动
主 Isolate: 已建立与 Worker 的双向通信
Worker Isolate: 等待任务...
Worker Isolate: 收到数据 [1, 2, 3, 4, 5]
主 Isolate: 收到结果 [1, 4, 9, 16, 25]
Worker Isolate: 收到数据 [10, 20, 30]
主 Isolate: 收到结果 [100, 400, 900]
Worker Isolate: 收到退出信号,关闭
主 Isolate: Worker 已退出
RUNOOB 双向通信演示结束

使用 Isolate.run 简化(Dart 3.0+)

Dart 3.0 引入了 Isolate.run(),大大简化了单次计算任务的使用。

实例

import 'dart:isolate';

// 一个耗时的计算函数
int complexCalculation(int n) {
  int result = 0;
  for (int i = 0; i < n; i++) {
    result += i * i;
  }
  return result;
}

Future<void> main() async {
  print('开始计算...');

  // Isolate.run:一行代码搞定,自动创建 Isolate、执行、返回结果
  var result = await Isolate.run(() => complexCalculation(10000000));
  print('RUNOOB 计算结果: $result');

  print('计算完成');

  // 对比:如果不使用 Isolate
  print('(如果在主 Isolate 中直接计算,会阻塞其他操作)');
}
开始计算...
RUNOOB 计算结果: 333333283333335000000
计算完成
(如果在主 Isolate 中直接计算,会阻塞其他操作)

Isolate.run() 适合"执行一次计算并返回结果"的场景。如果你需要持续的、双向的通信(比如长时间运行的后台服务),则需要使用 Isolate.spawn() + SendPort/ReceivePort。

Isolate 消息传递的限制

可以传递不能传递
null、bool、int、double、String函数、闭包
List、Map、Set(元素也可传递)Stream、Future
SendPort(用于建立通信链)大多数非基本类型的对象
Capability(权限令牌)文件句柄、网络 Socket
TransferableTypedData(高效传递大块数据)自定义类(除非可序列化)

并发模型对比

特性Dart Isolate传统多线程(如 Java)
内存模型独立内存,不共享共享内存
通信方式消息传递(SendPort)共享变量 + 锁
数据竞争不存在(无共享)需要锁机制保护
死锁风险几乎不存在存在
创建开销相对较大相对较小
适用场景CPU 密集型并行计算通用并发

Dart 的 Isolate 模型虽然避免了数据竞争,但代价是创建和通信的开销较大。不要创建成千上万个 Isolate——通常几个 Isolate 就足够了。对于 I/O 密集型任务,使用 async/await 是最佳选择。