在 Dart 中,Isolate
是一种用于并发处理的机制。它与线程类似,但与传统线程有所不同。每个 Isolate
都有自己的内存空间和事件循环,彼此之间无法直接共享内存,(有点多进程的概率)因此它们之间的通信需要通过消息传递(SendPort
和 ReceivePort
)来实现。
下面是 Dart 中 Isolate
使用的一些基本示例,包括创建 Isolate
、在多个 Isolate
之间传递消息以及如何管理它们的生命周期。
一、什么是事件循环
在 Dart 的线程中也存在事件循环和消息队列的概念,但在 Dart 中线程叫做isolate
。应用程序启动后,开始执行 main 函数并运行 main isolate
。
每个 isolate 包含一个事件循环以及两个事件队列,event loop事件循环
,以及event queue
和microtask queue事件队列
。
- event queue:负责处理I/O事件、绘制事件、手势事件、接收其他 isolate 消息等外部事件。
- microtask queue:可以自己向 isolate 内部添加事件,事件的优先级比 event queue高。不建议使用
二、 创建基本的 Isolate
1. 使用 Isolate.spawn
创建新的 Isolate
Isolate.spawn
是最常见的创建 Isolate
的方式,它用于启动一个新的 Isolate
并指定其执行的函数。这个方法接受一个执行函数和一个函数参数,执行函数将在新的 Isolate
中执行。
示例:创建一个新的 Isolate
import 'dart:isolate';
void sayHello(String message) {
print('Hello from Isolate: $message');
}
void main() async {
// 创建一个新的 Isolate,并将任务传递给它
await Isolate.spawn(sayHello, "ok");
print('main Isolate is running');
}
输出:
hello from isolate:ok
main Isolate is running
在这个示例中,Isolate.spawn
方法创建了一个新的 Isolate
,并让它执行 sayHello
函数,传递一个参数 "ok"
。
Isolate.spawn
需要传递两个参数:一个回调函数(即将要在新Isolate
中执行的函数)和参数(将传递给该函数)。Isolate.spawn
会在主Isolate
中返回一个Future
,在新的Isolate
完成执行后,Future
会被完成。
2. 使用 Isolate.spawnUri
从外部文件启动 Isolate
Isolate.spawnUri
允许从外部 Dart 文件中加载并执行一个新的 Isolate
。它的作用类似于 Isolate.spawn
,但是可以从指定的文件中加载要执行的代码。
//main.dart
import 'dart:isolate';
void main() async {
// 创建一个 ReceivePort,用来接收 worker.dart 的消息
final receivePort = ReceivePort();
// 启动一个新的 Isolate,传递 worker.dart 的 URI 和 ReceivePort 的 SendPort
final uri = Uri.parse('worker.dart'); // worker.dart 文件的 URI
await Isolate.spawnUri(uri, ['Hello', 'from', 'main'], receivePort.sendPort);
// 监听从 worker.dart 发来的消息
receivePort.listen((message) {
print('Received from worker: $message');
receivePort.close(); // 接收到消息后关闭 ReceivePort
});
print('Main Isolate is running...');
}
//worker.dart
import 'dart:isolate';
void main(List<String> args) {
// 打印收到的命令行参数
print('Worker received arguments: ${args.join(' ')}');
// 创建一个 ReceivePort,用于接收消息
final receivePort = ReceivePort();
// 向主线程发送一条消息
receivePort.sendPort.send('Hello from worker!');
// 监听主线程的消息(如果有的话)
receivePort.listen((message) {
print('Received from main: $message');
});
// 模拟一些耗时的工作
Future.delayed(Duration(seconds: 1), () {
receivePort.sendPort.send('Worker done!');
});
}
打印结果:
Main Isolate is running...
Worker received arguments: Hello from main
Received from worker: Hello from worker!
Received from main: Worker done!
进一步说明
- 命令行参数:在
worker.dart
中,args
参数是传递给main
函数的列表。在本例中,args
包含了从main.dart
传递过来的字符串数组['Hello', 'from', 'main']
。 - 消息传递:主线程通过
ReceivePort
接收来自子线程(worker.dart
)的消息。子线程通过其SendPort
向主线程发送消息。 - Isolate 独立性:
Isolate.spawnUri
启动的新Isolate
是完全独立的,与主线程的内存和上下文隔离,它们之间的通信只能通过消息传递(即SendPort
和ReceivePort
)来实现 - 主线程将自己的
ReceivePort
的sendPort
传递给新的Isolate
。这个SendPort
的作用是让主线程能够向新创建的Isolate
发送消息。 - worker.dart 中必须要有自己的 main 函数
三、 通过 SendPort 和 ReceivePort 进行通信
由于每个 Isolate
拥有独立的内存空间,它们之间不能直接共享数据。因此,Dart 提供了 SendPort
和 ReceivePort
来进行进程间通信。
示例:在 Isolate
之间传递消息
import 'dart:isolate';
void isolateEntry(SendPort sendPort) {
// 向主 Isolate 发送消息
sendPort.send('Hello from Isolate!');
}
void main() async {
// 创建 ReceivePort,主 Isolate 用来接收消息
final receivePort = ReceivePort();
// 创建新的 Isolate,并传递 SendPort
await Isolate.spawn(isolateEntry, receivePort.sendPort);
// 从 ReceivePort 获取消息
receivePort.listen((message) {
print('Received message: $message');
receivePort.close(); // 关闭 ReceivePort
});
print('Main Isolate is running.');
}
输出:
Isolate is running.
Received message: Hello from Isolate!
在这个示例中:
- 主 Isolate 创建了一个
ReceivePort
来接收消息。 - 新的
Isolate
通过sendPort.send
向主 Isolate 发送消息。 - 主 Isolate 通过
receivePort.listen
监听来自Isolate
的消息,并在收到消息时打印它。
四、 处理复杂数据类型
在 Isolate
之间传递的消息只能是可传递的消息(比如基本数据类型、List
、Map
等),而且它们会被复制到新的 Isolate
中。因此,传递复杂的对象(比如自定义类实例)时,需要进行序列化/反序列化。
1、发送一个复杂数据
示例:传递一个 Map
数据
import 'dart:isolate';
void processData(SendPort sendPort) {
// 发送一个包含多个字段的 Map
sendPort.send({
'name': 'Alice', 'age': 30});
}
void main() async {
final receivePort = ReceivePort();
await Isolate.spawn(processData, receivePort.sendPort);
// 监听接收到的消息
receivePort.listen((message) {
print('Received data: $message');
receivePort.close();
});
print('Main Isolate is running.');
}
输出:
Isolate is running.
Received data: {name: Alice, age: 30}
2、传递一个复杂数据
示例:传递一个 Map
数据
import 'dart:isolate';
void sendMessage2(Map map){
map['port'].send("来自 isolate 中的消息!");
}
void main() async {
final receivePort = ReceivePort();
final map = {
'name':'张三','port':receivePort.sendPort};
await Isolate.spawn(sendMessage2, map);
receivePort.listen((message){
print('收到消息:$message');
receivePort.close();
});
print('main Isolate is running');
}
输出:
hello from isolate:ok
main Isolate is running
收到消息:来自 isolate 中的消息!
五、并发执行多个 Isolate
可以同时启动多个 Isolate
,使得它们并行处理不同的任务。以下是一个示例,展示如何创建多个 Isolate
并执行不同的任务。
示例:启动多个 Isolate 进行并发计算
import 'dart:isolate';
void computeTask(Map map) {
int result = map['index'] * 2;
map['port'].send('Task result: $result');
}
void main() async {
final receivePort = ReceivePort();
// 启动多个 Isolate 来执行并行任务
for (int i = 1; i <= 5; i++) {
final map = {
'index': i, 'port': receivePort.sendPort};
await Isolate.spawn(computeTask, map);
}
// 监听来自各个 Isolate 的结果
receivePort.listen((message) {
print(message);
});
print('Main Isolate is running.');
}
输出:
Isolate is running.
Task result: 2
Task result: 4
Task result: 6
Task result: 8
Task result: 10
六、 捕获异常
Isolate
的执行过程中可能会抛出异常,你可以使用 Isolate.spawn
的 onError
参数来捕获这些异常。
示例:捕获异常
import 'dart:isolate';
void errorProneTask(SendPort sendPort) {
throw Exception('An error occurred in the Isolate');
}
void main() async {
final receivePort = ReceivePort();
// 传递错误处理的 ReceivePort
await Isolate.spawn(errorProneTask, receivePort.sendPort, onError: receivePort.sendPort);
// 监听错误信息
receivePort.listen((message) {
if (message is String && message.startsWith('Exception')) {
print('Caught error: $message');
} else {
print('Received message: $message');
}
});
print('Main Isolate is running.');
}
输出:
Isolate is running.
Caught error: Exception: An error occurred in the Isolate
七、在 Isolate
中使用 async
和 await
Isolate
的工作线程本身也可以使用 Dart 的异步机制。你可以在 Isolate
中执行异步操作,如网络请求等。
示例:在 Isolate
中使用异步操作
import 'dart:isolate';
import 'dart:async';
Future<void> asyncTask(SendPort sendPort) async {
await Future.delayed(Duration(seconds: 2));
sendPort.send('Task completed');
}
void main() async {
final receivePort = ReceivePort();
// 启动一个 Isolate 执行异步任务
await Isolate.spawn(asyncTask, receivePort.sendPort);
// 监听异步任务结果
receivePort.listen((message) {
print(message); // 输出 "Task completed"
receivePort.close(); // 关闭 ReceivePort
});
print('Main Isolate is running.');
}
输出:
Isolate is running.
Task completed
总结
通过上述示例,你可以看到 Dart 中的 Isolate
如何在并行计算和并发处理方面发挥作用。每个 Isolate
都是一个独立的执行单元,它拥有自己的内存和事件循环,因此不能直接共享数据。相反,数据交换需要通过 SendPort
和 ReceivePort
进行。Isolate
是一种高效的并发机制,适用于计算密集型任务,避免了线程安全问题并有效利用多核处理器。
参考连接:https://juejin.cn/post/7039115158261596191