Skip to content

以下是 Dart 中 并发编程(Concurrent Programming) 的概念性概述,涵盖 事件循环(Event Loop)异步语言特性(async/await、Future、Stream) 以及 隔离区(Isolates) 的核心机制。此内容适用于理解 Dart 的并发模型,若需实际代码示例,请参考官方的 Asynchronous ProgrammingIsolates 页面。


🔄 事件循环(Event Loop)

Dart 的运行时基于 单线程事件循环模型。所有代码(包括 UI 响应、I/O、定时器等)都通过一个 事件队列(event queue) 依次处理:

dart
while (eventQueue.waitForEvent()) {
  eventQueue.processNextEvent();
}
  • 事件来源:UI 重绘、用户输入、网络响应、文件读取等。
  • 处理方式:先进先出(FIFO),一次只处理一个事件。
  • 关键点:Dart 是单线程的,但通过 异步 API 实现“并发”效果——在等待 I/O 时让出 CPU,处理其他事件。

✅ 异步操作(如 http.get())会立即返回一个 Future,并将回调注册到事件循环中,待结果就绪后再执行。


⏳ 异步编程(Asynchronous Programming)

1. Future

表示一个未来会完成的异步操作(成功返回值,或失败抛出错误)。

dart
Future<String> _readFileAsync(String filename) {
  return File(filename).readAsString().then((contents) => contents.trim());
}

2. async / await

提供声明式语法,让异步代码看起来像同步代码:

dart
void main() async {
  final data = await _readFileAsync();
  print(jsonDecode(data));
}

Future<String> _readFileAsync() async {
  final contents = await File('file.txt').readAsString();
  return contents.trim();
}

⚠️ await 只能在标记为 async 的函数中使用。

3. Stream

用于处理随时间多次发出的值(如传感器数据、定时器、WebSocket 消息)。

dart
Stream<int> squares = Stream.periodic(Duration(seconds: 1), (i) => i * i);

配合 await foryield 实现流式处理:

dart
Stream<int> sumStream(Stream<int> stream) async* {
  var sum = 0;
  await for (final value in stream) {
    yield sum += value;
  }
}

🧱 隔离区(Isolates)

Dart 的真正并发机制:Isolates

核心特性:

  • 每个 isolate 有 独立内存空间(无共享状态)。
  • 每个 isolate 有 自己的事件循环
  • 通信仅通过 消息传递(message passing),使用 SendPortReceivePort
  • 基于 Actor 模型,避免数据竞争、锁、死锁等问题。

主隔离区(Main Isolate)

  • 所有 Dart 程序从 main()main isolate 中启动。
  • 即使单 isolate 程序也能流畅运行,只要合理使用 async/await 避免阻塞事件循环。

❗ 长时间同步计算(如递归斐波那契)会阻塞事件循环,导致 UI 卡顿。

后台工作器(Background Workers)

将耗时任务(如大 JSON 解析、加密、复杂计算)移至 worker isolate

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

void fib40() async {
  var result = await Isolate.run(() => slowFib(40)); // 在新 isolate 中运行
  print('Fib(40) = $result');
}

使用方式

方法适用场景
Isolate.run()推荐:执行一次性计算,自动管理生命周期
Isolate.spawn()长期运行的 isolate,需手动管理消息和退出
Isolate.spawnUri()从独立 URI 启动 isolate(慢,不共享代码)

💡 Isolate.run() 是现代 Dart 中最简洁、安全的后台任务方式。


⚠️ Isolate 的限制与注意事项

1. 不是线程

  • 全局变量在不同 isolate 中是完全独立的副本
  • 修改 worker isolate 中的变量不会影响 main isolate。

2. 可发送的消息类型

可通过 SendPort.send() 发送大多数 Dart 对象,但以下类型不可发送

  • Socket, ReceivePort, DynamicLibrary
  • Pointer, Finalizer, UserTag
  • 标记了 @pragma('vm:isolate-unsendable') 的类实例

3. 同步阻塞通信限制

  • 虽然可创建数百个 isolate,但并行执行的 isolate 数量受 VM 堆大小限制
  • 在 FFI(C 互操作)中进行同步阻塞调用可能导致死锁。
  • 解决方案:在 C 代码中调用 Dart_ExitIsolate() 释放当前 isolate,操作完成后再 Dart_EnterIsolate()

🌐 Web 平台上的并发

  • Dart Web 不支持 Isolates
  • 替代方案:Web Workers
    • 功能类似,但需单独编译入口文件(类似 spawnUri)。
    • 消息传递通过数据拷贝(无内存转移优化),大消息性能较差。
    • Isolate.spawn() 的轻量级复用机制。

✅ 所有平台(包括 Web)都支持 FutureStreamasync/await


📚 建议资源

  • Actor Model(Isolate 的理论基础)
  • Flutter: IsolateNameServer(跨 isolate 命名通信)
  • 非 Flutter: package:isolate_name_server
  • 官方 API 文档:
    • Isolate.spawn()
    • Isolate.exit()
    • SendPort / ReceivePort

✅ 总结

机制用途是否跨核心是否共享内存
Future / Stream / async-awaitI/O 并发、非阻塞操作❌(单线程)✅(同 isolate)
IsolateCPU 密集型任务、真正并行✅(多核)❌(消息传递)

🎯 最佳实践

  • I/O 操作 → 用 async/await
  • 计算密集型任务 → 用 Isolate.run()
  • 避免在 main isolate 中做长时间同步计算
  • Web 开发中用 Web Workers 替代 Isolates