Dart 语言的事件循环(Event Loop)处理机制、异步处理和并发编程的原理和使用方法
- Dart 是单线程的,但单线程和异步并不冲突
- Flutter 使用事件循环来处理各种异步操作,在 Dart 中实际上有两个队列:
事件队列(Event Queue)和微任务队列(Microtask Queue) - 在每一次的事件循环中,Dart 总是先去第一个微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。因为微任务队列在事件循环中的优先级是最高的,只要队列中还有任务,就会一直霸占循环
- Dart 为 Event Queue 的任务建立提供了一层封装,叫作
Future
/*
then与Future函数体共用一个事件循环。如果Future有多个then,会按照链式斯奥用的先后顺序同步执行
*/
Future(() => print('num1'));
Future(() => print('num2'))
.then((_) => print('num3'))
//执行结果:num1 num2 num3
/*
第一个语句中Future与then共用一个事件循环,所以执行完num1之后立刻同步执行num2
第二个语句中Future的函数体是null,意味着没有不需要也没有事件循环,后面的then无法共享一个事件循环。
这种情况下Dart会把后续的then放入微任务队列,在下一次事件循环中执行
*/
Future(() => print('num1')).then((_) => print('num2'));
Future(() => null).then((_) => print('num3'));
then会在 Future 函数体执行完毕后立刻执行,无论是共用同一个事件循环还是进入下一个微任务- Dart 中的
await并不是阻塞等待,而是异步等待。在事件循环中,Dart 会将等待语句放入事件队列中,一旦有结果,事件循环就会把它从事件队列中取出执行
/*
*/
Future(() => print('num1'))
.then((_) async => await Future(() => print('num2')))
.then((_) => print('num3'));
Future(() => print('num4'));
//执行结果: num1 num4 num2 num3
- 尽管 Dart 采用单线程模型作为核心设计,但为充分释放多核 CPU 性能、高效处理 CPU 密集型任务,Dart 也提供了多线程机制
Isolate。在 Isolate 中,资源隔离做得非常好,每个 Isolate 都有自己的事件循环与队列,Isolate 之间不共享任何资源,只能依靠消息机制通信,因此也就没有资源抢占问题。
/*
在并发 Isolate 中,用管道给主 Isolate 发了一个字符串,实现***基本 Isolate 通信***
*/
// 用于接收 Isolate 消息的端口
ReceivePort? _receivePort;
// 存储所有执行结果的列表
List<String> _results = [];
// 基本 Isolate 通信示例
Future<void> _basicIsolateDemo() async {
setState(() {
_results.clear();//清空之前的结果
});
try{
// 创建接收端口
_receivePort = ReceivePort();
// 创建新的 Isolate,传入发送端口
// _getMsg 是在 Isolate 中执行的函数
_currentIsolate = await Isolate.spawn(_getMsg, _receivePort!.sendPort);
// 监听来自 Isolate 的消息
_receivePort!.listen((data) {
setState(() {
print('收到消息: $data');
_results.add('收到消息: $data');
});
_cleanupIsolate(); // 通信完成后清理资源
});
}catch (e){
print('错误: $e');
}
}
//并发Isolate往管道发送一个字符串
void _getMsg(SendPort sendPort) {
sendPort.send("Hello from Isolate!");
}
/// 清理 Isolate 资源的方法
/// 关闭接收端口,终止 Isolate,并重置相关变量
void _cleanupIsolate() {
_receivePort?.close(); // 关闭接收端口
_currentIsolate?.kill(priority: Isolate.immediate); // 立即终止 Isolate
_currentIsolate = null; // 重置 Isolate 引用
_receivePort = null; // 重置端口引用
}
/*
在并发 Isolate 中,用管道给主 Isolate 发了一个字符串,实现***双向 Isolate 通信***
*/
// 用于接收 Isolate 消息的端口
ReceivePort? _receivePort;
// 存储所有执行结果的列表
List<String> _results = [];
// 基本 Isolate 通信示例
Future<void> _bidirectionalCommunicationDemo() async {
setState(() {
_results.clear();//清空之前的结果
});
try{
// 创建接收端口
_receivePort = ReceivePort();
// 创建新的 Isolate,传入发送端口
// _getMsg 是在 Isolate 中执行的函数
_currentIsolate = await Isolate.spawn(_getMsg, _receivePort!.sendPort);
// 监听来自 Isolate 的消息
_receivePort!.listen((data) {
setState(() {
print('收到消息: $data');
_results.add('收到消息: $data');
});
_cleanupIsolate(); // 通信完成后清理资源
});
}catch (e){
print('错误: $e');
}
}
// 双向通信工作线程
// 演示主线和 Isolate 之间的双向通信
// 支持接受任务指令和发送进度更新
void _bidrectionalWork(SendPort sendPoer){
// 创建接收端口,用于接受来自主线程的消息
final receicerPort = ReceivePort();
// 将接收端口发送端发送给主线程,建立双向通信
endPort.send(receivePort.sendPort);
// 监听来自主线程的消息
receivePort.linsten((data){
if(data is Map && ['type'] == 'start'){
// 接收到开始任务的指令
final value = data['value'] as int;
// 模拟长时间运行的任务,定期发送进度更新
for(int i = 0; i <= value; i += 10 ){
// 发送进度更新到主线程
sendPort.send({
'type': 'progress',
'progress': (i / value * 100).round(),
});
// 模拟实际工作(空循环)
for(int j = 0; j <= 1000000; j++){
// 空循环模拟计算密集型工作
}
}
sendPort.send({
'type': 'result',
'result': '任务完成,处理了 $value 个项目',
});
}
});
}
- 在 Flutter 中处理并发计算任务时,可以采用更为简洁的方式 —— 使用框架提供的
compute函数。compute函数是对 Isolate 机制的高阶封装,它内部自动完成了 Isolate 的创建、销毁以及双向消息通信等底层操作,大大简化了并发编程的复杂度。使用时,只需传入两个核心参数:一个需要在后台执行的计算函数(作为任务入口)和该函数所需的。compute会自动将任务分配到新的 Isolate 中执行,计算完成后通过返回值将结果传递回主线程,全程无需关注 Isolate 的生命周期管理和消息机制细节,让开发者能更专注于业务逻辑实现。
import 'package:flutter/foundation.dart';
// 1. 定义需要在后台执行的耗时计算函数
// 注意:必须是顶级函数或静态函数
int calculateFactorial(int number) {
print('开始计算 $number 的阶乘...');
int result = 1;
for (int i = 2; i <= number; i++) {
result *= i;
// 模拟计算过程
if (i % 5 == 0) {
print('已计算到: $i');
}
}
return result;
}
// 2. 定义调用compute的函数
Future<int> performBackgroundCalculation(int input) async {
// 使用compute启动后台计算
// 参数1: 要执行的计算函数
// 参数2: 传递给计算函数的参数
final result = await compute(calculateFactorial, input);
return result;
}
// 3. 演示如何使用
void main() async {
print('程序开始');
// 启动后台计算并获取结果
final factorialResult = await performBackgroundCalculation(20);
print('计算完成: 20的阶乘是 $factorialResult');
print('程序结束');
}
总结
- Dart 采用单线程模型,依托事件循环实现异步能力。Future 结合
async/await,在事件循环调度下实现非阻塞的同步化编程。 Isolate是独立并发单元,拥有专属内存、事件循环及任务队列,实现完全资源隔离。Isolate 间无共享状态,通过基于端口的消息传递通信(采用序列化拷贝),保障状态安全。Isolate间消息会被封装为事件插入接收方队列,由其事件循环调度处理,形成跨 Isolate 协作式并发,既规避共享内存的线程安全问题,又保持异步编程模型一致性。