单线程事件队列
Dart程序是单线程+事件驱动方式运行,Dart代码的运行就是在不停的在处理一个又一个的事件
而 Dart 的单线程跟 Java 的完全不同,虽然都是单线程,但是 Dart 分为三个形式:
主线程
微任务队列
事件任务队列
主线程
跟 Java 一样,具有唯一性,也就是从main()
开始的线程。
微任务队列
表示一个短时间内就会完成的异步任务,它的优先级比事件队列高。
主要是通过scheduleMicrotask
进行调度。
事件任务队列
包含所有的外来事件,比如:I/O、手势、绘图等。,例如:I/O 事件任务、Timer 事件任务等。
Flutter 任务队列的执行图:

优先级
1 主线程 > 微任务队列 > 事件任务队列。
所以,在 Dart 单线程中,会优先执行完主线程,在执行主线程的过程中,若发现微任务或者事件任务,就会将他们放入相应的任务队列中,然后就会一个个执行完微任务队列里面的微任务,其次再去执行事件任务队列里面的事件任务。
示例代码如下
Timer.run(() {
print('Timer run');
});
scheduleMicrotask(() {
print('scheduleMicrotask run');
});
print('main run');
打印输出,可以看出,即使我们把事件任务队列放在最前面,它也是最后执行
main run
scheduleMicrotask run
Timer run
Process finished with exit code 0
再来个复杂例子,在事件任务里面添加微任务,在微任务里面添加事件任务
//主线程
print('main start!');
//事件任务
Timer.run(() {
print('事件任务 start!');
//子微任务
scheduleMicrotask(() {
print('子微任务执行!');
});
print('事件任务 end!');
});
//微任务
scheduleMicrotask(() {
print('微任务 start!');
//子事件任务
Timer.run(() {
print('微任务中子事件任务执行!');
});
scheduleMicrotask((){
print('微任务微子事件任务执行!');
});
print('微任务 end!');
});
print('main end!');
输出
main start!
main end!
微任务 start!
微任务 end!
微任务微子事件任务执行!
事件任务 start!
事件任务 end!
子微任务执行!
微任务中子事件任务执行!
首先是main任务执行完,然后再执行微任务,等微任务完成后,再执行事件任务,等事件任务执行完,再微任务,事件任务。
添加sleep延迟
//主线程
print('main start!');
sleep(Duration(seconds: 2));
//微任务
scheduleMicrotask((){
print('微任务 start!');
sleep(Duration(seconds: 2));
//子事件任务
Timer.run(() {
print('子事件任务执行!');
sleep(Duration(seconds: 2));
});
print('微任务 end!');
});
//事件任务
Timer.run(() {
print('事件任务 start!');
sleep(Duration(seconds: 2));
//子微任务
scheduleMicrotask((){
print('子微任务执行!');
sleep(Duration(seconds: 2));
});
print('事件任务 end!');
});
print('main end!');
输出如下,由于dart本来就是单线程,,隐藏sleep后,后面的任务都暂停了,因此任务顺序不变
main start!
main end!
微任务 start!
微任务 end!
事件任务 start!
事件任务 end!
子微任务执行!
子事件任务执行!
结论: 在 Dart 单线程中,主线程、微任务队列、事件任务队列是线性执行的,后面的队列必须等到前面队列都运行完后再执行
多线程
在一个页面中做耗时比较大的运算时,就算用了 async、await 异步处理,UI页面的动画还是会卡顿,因为还是在这个UI线程中做运算,异步只是你可以先做其他,等我这边有结果再返回,但是,我们的计算仍旧是在这个UI线程,仍会阻塞UI的刷新,异步只是在同一个线程的并发操作。所以这个时候就需要创建新的线程来执行耗时操作解决这个问题。
什么是 Isolate
Isolate 是 Dart 平台对线程的实现方案,但和普通 Thread 不同的是,isolate 拥有独立的内存,isolate 由线程和独立内存构成。正是由于 isolate 线程之间的内存不共享,所以 isolate 线程之间并不存在资源抢夺的问题,所以也不需要锁。通过 isolate 可以很好的利用多核 CPU,来进行大量耗时任务的处理。
print('main start');
Isolate.spawn((msg){
print('spawn 1');
print(msg);
},'1');
Isolate.spawn((msg){
print('spawn 2');
print(msg);
},'2');
Isolate.spawn((msg){
print('spawn 3');
print(msg);
},'3');
print('main end');
输出如下(每次输出不一样)
main start
spawn 1
1
main end
spawn 3
3
spawn 2
2
Isolate 通信机制
isolate 之间的通信主要通过 SendPort和ReceivePort来进行。主要流程是:先初始化一个ReceivePort,然后调用listen就可以监听其他isolate发送的消息。
那其他isolate如何给当前isolate发送消息?答案就是获取到ReceivePort的sendPort。
ReceivePort:接收其他Isolate数据
SendPort:发送给其他Isolate数据
代码如下,由于生成Isolate的spawn方法第二个参数就是传递给Isolate的数据,因此我们把主isolate的SendPort发送过去,这样子isolate获取到主Isolate的SendPort,就可以调用sendPort的send方法给主Isolate发送数据,主Isolate就可以接收到。
那主Isolate除了创建时给子Isolate发送数据,后续如何给子Isolate继续发送数据呢?
这就要求子Isolate把它自己的SendPort发送给主Isolate
print('main start');
ReceivePort port = ReceivePort();
Isolate iso = await Isolate.spawn<SendPort>((message) {
print('子iso:发送消息gaga');
message.send('gaga');
print('子iso:发送成功');
ReceivePort iosPort = ReceivePort();
message.send(iosPort.sendPort);
iosPort.listen((msg) {
print('子iso:$msg');
});
print('子iso:发送port成功');
}, port.sendPort);
port.listen((message) {
print('主iso:收到子iso $message');
if(message is SendPort){
message.send('主线程发送子线程消息');
port.close();
}
});
print('main end');
输出如下
main start
main end
子iso:发送消息gaga
子iso:发送成功
子iso:发送port成功
主iso:收到子iso gaga
主iso:收到子iso SendPort
子iso:主线程发送子线程消息
参考文章:
Dart单线程理解(与Java完全不同)
Flutter异步编程-Isolate
深入理解Flutter/Dart事件机制
网友评论