flutter/dart中没有多线程,只有单线程。WTF?其实前面说的多线程在它里面只是换了个名字:隔离(isolate / ˈaɪsəleɪt /)。
主线程(UI线程)
flutter默认是在单线程(主线程)运行,它里面包含2个队列:
- 微任务队列(
Microtask Queue) - 事件/任务/宏队列(
Event Queue)
所以加上默认的主线程Main Queue是3个队列。
执行顺序
主线程所在的代码会同步执行Main Queue,然后再执行它里面的微任务Microtask Queue与事件/任务队列Event Queue。
比如:
void main() {
// 进入事件队列。如果有多个加入事件队列,按加入事件队列的顺序先后执行。
Future((){
print('Future');
});
// 进入主线程。代码从上到下依次执行
print('main');
// 进入微任务队列。如果有多个加入微任务队列,按加入微任务队列的顺序先后执行。
scheduleMicrotask((){
print('scheduleMicrotask');
});
}
执行后输出:
flutter: main
flutter: scheduleMicrotask
flutter: Future
如何另外启动一个线程呢?
在flutter/dart里,另外一个线程又叫做隔离(isolate),它是真的内存隔离,所以互相之间交互需要引入一个信使(port)。根据用途,我们可以大概的分为三种:
- 手动创建Isolate(完整控制)
- 只能传递可序列化的数据
- 不能传递函数闭包
- 不能直接共享内存
Future<void> manualIsolate() async {
// 创建接收消息的端口
final receivePort = ReceivePort();
// 启动新的isolate
final isolate = await Isolate.spawn(
workerFunction, // isolate中要执行的函数
receivePort.sendPort, // 传递给worker的发送端口
);
// 监听接收到的消息
receivePort.listen((message) {
if (message is String) {
print('收到消息: $message');
} else if (message == 'done') {
// 清理工作
receivePort.close();
isolate.kill();
}
});
}
// 注意:worker函数必须是顶级函数或静态方法
void workerFunction(SendPort sendPort) {
// 在这里执行耗时操作
for (var i = 0; i < 5; i++) {
sendPort.send('处理进度: $i');
}
sendPort.send('done');
}
- compute 方式(最简单)
- 实际上是对Isolate.spawn的封装
- 自动处理了消息传递和清理
- 适合一次性的计算任务
// compute 实际上是对 Isolate 的封装,适合一次性的计算任务
Future<List<String>> processData(List<String> data) async {
final result = await compute(
// 第一个参数:要在新isolate中执行的顶级函数或静态方法
(List<String> input) {
// 这里的代码在新的isolate中执行
return input.map((e) => e.toUpperCase()).toList();
},
// 第二个参数:传递给函数的参数
data,
);
return result;
}
- 双向通信的Isolate(复杂场景)
class IsolateManager {
late Isolate isolate;
late ReceivePort receivePort;
late SendPort workerSendPort;
Future<void> start() async {
receivePort = ReceivePort();
// 启动worker isolate
isolate = await Isolate.spawn(
workerFunction,
receivePort.sendPort,
);
// 等待worker发送它的SendPort
workerSendPort = await receivePort.first;
// 设置消息处理
receivePort.listen((message) {
if (message is SendPort) return; // 忽略初始的SendPort消息
print('收到worker消息: $message');
});
}
void sendMessage(String message) {
workerSendPort.send(message);
}
void dispose() {
receivePort.close();
isolate.kill();
}
}
void workerFunction(SendPort mainSendPort) {
// worker的接收端口
final receivePort = ReceivePort();
// 首先发送worker的SendPort给main isolate
mainSendPort.send(receivePort.sendPort);
// 处理接收到的消息
receivePort.listen((message) {
print('Worker收到消息: $message');
// 可以处理后发送结果回去
mainSendPort.send('处理结果: $message');
});
}
// 使用示例
void example() async {
final manager = IsolateManager();
await manager.start();
// 发送消息给worker
manager.sendMessage('Hello Worker');
// 完成后清理
await Future.delayed(Duration(seconds: 5));
manager.dispose();
}
另外再额外补充可能一个用到的场景:
- 持久化的后台Worker(长期运行的任务)
class BackgroundWorker {
late Isolate isolate;
late ReceivePort receivePort;
late SendPort workerSendPort;
final _taskQueue = Queue<Task>();
bool _isProcessing = false;
Future<void> initialize() async {
receivePort = ReceivePort();
isolate = await Isolate.spawn(
workerFunction,
receivePort.sendPort,
);
workerSendPort = await receivePort.first;
_setupMessageHandler();
}
void _setupMessageHandler() {
receivePort.listen((message) {
if (message is SendPort) return;
if (message == 'ready') {
_processNextTask();
} else {
// 处理任务结果
print('任务结果: $message');
_isProcessing = false;
_processNextTask();
}
});
}
void addTask(Task task) {
_taskQueue.add(task);
if (!_isProcessing) {
_processNextTask();
}
}
void _processNextTask() {
if (_taskQueue.isEmpty) return;
_isProcessing = true;
final task = _taskQueue.removeFirst();
workerSendPort.send(task);
}
}
class Task {
final String type;
final Map<String, dynamic> data;
Task(this.type, this.data);
}
// Worker实现
void workerFunction(SendPort mainSendPort) {
final receivePort = ReceivePort();
mainSendPort.send(receivePort.sendPort);
receivePort.listen((message) async {
if (message is Task) {
// 处理任务
final result = await _processTask(message);
mainSendPort.send(result);
}
// 表示准备接收下一个任务
mainSendPort.send('ready');
});
}
// 使用示例
void main() async {
final worker = BackgroundWorker();
await worker.initialize();
worker.addTask(Task('process', {'data': 'some data'}));
worker.addTask(Task('convert', {'file': 'path/to/file'}));
}
那这几种我们什么场景下又该选用谁?没有选择困难症:
- 短任务用compute
- 需要状态管理用手动Isolate
- 需要双向通信用ReceivePort/SendPort
- 长期运行任务用持久化Worker
如何传递复杂对象?
前面说只能传递可序列化的数据?复杂对象自己序列化好就可以了。这里用compute方式举例
// ❌ 错误示例:不能传递无法序列化的对象
Future<void> wrongUsage() async {
final controller = StreamController();
await compute(
(StreamController ctrl) {
// 错误!StreamController无法序列化
ctrl.add('data');
},
controller, // 这会抛出错误
);
}
// ❌ 错误示例:不能使用闭包
String globalVar = 'test';
Future<void> wrongClosure() async {
final localVar = 'local';
await compute(
() {
// 错误!无法访问外部变量
return globalVar + localVar;
},
null,
);
}
// ✅ 正确示例:传递所需的所有数据
Future<String> correctUsage() async {
final data = {
'global': globalVar,
'local': 'local',
};
return compute(
(Map<String, String> input) {
return input['global']! + input['local']!;
},
data,
);
}
// ✅ 一个处理复杂对象的更完整的示例
class ComplexData {
final String name;
final int value;
ComplexData(this.name, this.value);
// 注意:需要能被序列化
Map<String, dynamic> toMap() => {
'name': name,
'value': value,
};
factory ComplexData.fromMap(Map<String, dynamic> map) => ComplexData(
map['name'],
map['value'],
);
}
Future<ComplexData> processComplexData(ComplexData data) async {
// 传递复杂对象时需要序列化
final result = await compute(
(Map<String, dynamic> input) {
final data = ComplexData.fromMap(input);
// 处理数据
return ComplexData(
data.name.toUpperCase(),
data.value * 2,
).toMap();
},
data.toMap(),
);
return ComplexData.fromMap(result);
}
优化性能的可选方式推荐
如果数据量太大,也可以分片处理。
// 批处理优化
Future<List<String>> optimizedBatchProcess(List<String> items) async {
// 将大量数据分块处理
final chunks = _splitIntoChunks(items, 1000);
// 并行处理多个块
final futures = chunks.map((chunk) =>
compute(
(List<String> input) => processChunk(input),
chunk,
)
);
// 等待所有处理完成并合并结果
final results = await Future.wait(futures);
return results.expand((x) => x).toList();
}
List<List<T>> _splitIntoChunks<T>(List<T> list, int chunkSize) {
final result = <List<T>>[];
for (var i = 0; i < list.length; i += chunkSize) {
result.add(list.sublist(
i,
i + chunkSize > list.length ? list.length : i + chunkSize,
));
}
return result;
}
// 缓存计算结果
class ComputeCache {
static final _cache = <String, dynamic>{};
static Future<T> computeWithCache<T>(
String key,
Future<T> Function() computation,
) async {
if (_cache.containsKey(key)) {
return _cache[key] as T;
}
final result = await computation();
_cache[key] = result;
return result;
}
}