以下是 Dart 中 并发编程(Concurrent Programming) 的概念性概述,涵盖 事件循环(Event Loop)、异步语言特性(async/await、Future、Stream) 以及 隔离区(Isolates) 的核心机制。此内容适用于理解 Dart 的并发模型,若需实际代码示例,请参考官方的 Asynchronous Programming 和 Isolates 页面。
🔄 事件循环(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 for 和 yield 实现流式处理:
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),使用
SendPort和ReceivePort。 - 基于 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,DynamicLibraryPointer,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)都支持
Future、Stream、async/await。
📚 建议资源
- Actor Model(Isolate 的理论基础)
- Flutter:
IsolateNameServer(跨 isolate 命名通信) - 非 Flutter:
package:isolate_name_server - 官方 API 文档:
Isolate.spawn()Isolate.exit()SendPort/ReceivePort
✅ 总结
| 机制 | 用途 | 是否跨核心 | 是否共享内存 |
|---|---|---|---|
Future / Stream / async-await | I/O 并发、非阻塞操作 | ❌(单线程) | ✅(同 isolate) |
Isolate | CPU 密集型任务、真正并行 | ✅(多核) | ❌(消息传递) |
🎯 最佳实践:
- I/O 操作 → 用
async/await- 计算密集型任务 → 用
Isolate.run()- 避免在 main isolate 中做长时间同步计算
- Web 开发中用 Web Workers 替代 Isolates