也许你会问,既然Dart是一种单线程语言,那么是不是就意味着Dart无法并发处理异步任务了?此言差矣。前面说到,所有的Dart程序都在Isolate
中运行,每个Isolate
拥有自己的私有内存块和一个事件循环模型
,其中,事件循环模型
就是用来处理各种事件,比如点输入/输出,点击,定时器以及异步任务等。下图描述了一个Isolate
的事件循环模型
的整个流程:
从上图可知,Dart事件循环机制由一个消息循环(event looper)
和两个消息队列
构成,其中,两个消息队列是指事件队列(event queue)
和微任务队列(Microtask queue)
。该机制运行原理为:
- 首先,Dart程序从main函数开始运行,待main函数执行完毕后,event looper开始工作;
- 然后,event looper优先遍历执行Microtask队列所有事件,直到Microtask队列为空;
- 接着,event looper才遍历执行Event队列中的所有事件,直到Event队列为空;
- 最后,视情况退出循环。
为了进一步理解,我们解释下上述三个概念:
(1)消息循环(Event Looper
)
顾名思义,消息循环就是指一个永不停歇且不能阻塞的循环,它将不停的尝试从微任务队列
和事件队列
中获取事件(event)进行处理,而这些Event包括了用户输入,点击,Timer,文件IO等。
(2)事件队列(Event queue
)
该队列的事件来源于外部事件
和Future
,其中,外部事件
主要包括I/O,手势,绘制,计时器和isolate相互通信的message等,而Future
主要是指用户自定义的异步任务,通过创建Future类实例来向事件队列添加事件。需要注意的是,当Event looper正在处理Microtask Queue
时,Event queue
会被阻塞,此时APP将无法进行UI绘制,响应用户输入和I/O等事件。下列示例演示了向Event queue
中添加一个异步任务事件:
main(List<String> args) {
print('main start...')
var futureInstance = Future<String>(() => "12345");
futureInstance.then((res) {
print(res);
}).catchError((err) {
print(err);
});
print('main end...')
}
(3)微任务队列(Microtask queue
)
大多数计算机中,甚至在移动平台上,都在使用多核CPU。 为了有效利用多核性能,开发者一般使用共享内存数据来保证多线程的正确执行。 然而多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。Dart作为一种新语言,为了缓解上述问题,提出了Isolate(隔离区)
的概念,即Dart没有线程的概念,只有Isolate
,所有的Dart代码都是在Isolate
中运行,它就像是机器上的一个小空间,具有自己的私有内存堆和一个运行着Event Looper的单个线程。
通常,一个Dart应用对应着一个Main Isolate
,且应用的入口即为该Isolate的main函数。当然,我们也可以创建其它的Isolate
,由于Isolate
的内存堆是私有的,因此这些Isolate
的内存都不会被其它Isolate
访问。假如不同的Isolate
需要通信(单向/双向),就只能通过向对方的事件循环队列里写入任务,并且它们之间的通讯方式是通过port(端口)
实现的,其中,Port又分为receivePort(接收端口)
和sendPort(发送端口)
,它们是成对出现的。Isolate之间通信过程:
- 首先,当前
Isolate
创建一个ReceivePort
对象,并获得对应的SendPort
对象;
var receivePort = ReceivePort();
var sendPort = receivePort.sendPort;
- 其次,创建一个新的
Isolate
,并实现新Isolate
要执行的异步任务,同时,将当前Isolate的SendPort对象传递给新的Isolate,以便新Isolate使用这个SendPort对象向原来的Isolate发送事件;
var anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
void otherIsolateInit(SendPort sendPort) async {
value = "Other Thread!";
sendPort.send("BB");
}
- 第三,调用当前Isolate#receivePort的listen方法监听新的Isolate传递过来的数据。Isolate之间什么数据类型都可以传递,不必做任何标记。
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date");
});
anotherIsolate?.kill(priority: Isolate.immediate);
anotherIsolate =null;
示例代码如下(Isolate单向通信):
import 'dart:isolate';
var anotherIsolate;
var value = "Now Thread!";
void startOtherIsolate() async {
var receivePort = ReceivePort();
anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date,value = $value");
});
}
void otherIsolateInit(SendPort sendPort) async {
value = "Other Thread!";
sendPort.send("BB");
}
import 'DartLib.dart';
void main(){
startOtherIsolate();
}
执行结果:
Isolate 1 接受消息:data = BB,value = Now Thread!
请发表评论