Dart是单线程执行,也就是说一旦Dart函数开始执行,就会一直持续直到结束,Dart函数不能被其他Dart代码中断。
注意:Dart命令行应用程序可以通过创建isolate来并行运行代码(Dart Web应用程序目前无法创建其他ioslate,但它们可以创建web worker)。 isolate不共享内存,它们就像是通过传递消息相互通信的独立应用程序。 除了应用程序明确在其他isolate或工作程序中运行的代码之外,所有应用程序的代码都在应用程序的main isolate中运行。
Html5 中的web worker
传统页面中(HTML5 之前)的 JavaScript 的运行都是以单线程的方式工作的,虽然有多种方式实现了对多线程的模拟(例如:JavaScript 中的 setinterval 方法,setTimeout 方法等),但是在本质上程序的运行仍然是由 JavaScript 引擎以单线程调度的方式进行的。在 HTML5 中引入的工作线程使得浏览器端的 JavaScript 引擎可以并发地执行 JavaScript 代码,从而实现了对浏览器端多线程编程的良好支持。 传统上的线程可以解释为轻量级进程,它和进程一样拥有独立的执行控制,一般情况下由操作系统负责调度。而在 HTML5 中的Web worker是这样一种机制,它允许在 Web 程序中并发执行多个 JavaScript 脚本,每个脚本执行流都称为一个线程,彼此间互相独立,并且有浏览器中的 JavaScript 引擎负责管理。
Event loops and queues能够确保同时处理多个图形操作或者事件。event loops的工作就是从event queue内拿一个event然后处理它,一直重复这个操作直到queue里全部处理完毕。event queue内的event有可能是用户输入事件、文件I/O通知、timers等等
如下图,Dart应用程序在其main isolate执行应用程序的main()函数时开始执行。 main()退出后,main isolate的线程开始逐个处理应用程序events queues的项目。
一个Dart应用程序只有一个event loop,但是有两个Queue:event queue和microtask queue:
Event queue包含来自Dart和系统中其他的事件。 目前,Microtask queue仅包含源自Dart代码的内容。
如下图所示,当main()退出时,Event loop开始工作。 首先,它按FIFO顺序执行所有microtasks。 然后它出列并处理event queue中的第一项。 然后它重复循环:执行所有microtasks,然后处理event queue中的下一项。 一旦两个队列都为空并且不再需要更多事件,应用程序的embedder(例如浏览器或测试框架)就可以dispose该应用程序。
这就是app运行时一个isolate中的正常运行流程。
注意:当Event Looper正在处理Microtask Queue中的Event时候,Event Queue中的Event就停止了处理了,此时App不能绘制任何图形,不能处理任何鼠标点击,不能处理文件IO等等
虽然可以预测task执行的顺序,但您无法准确预测event loop何时将任务从队列中删除。 Dart事件处理系统基于单线程循环; 它不是基于刻度或任何其他类型的时间测量。 例如,在创建延迟任务时,event会在您指定时排队。 但是,直到处理队列中的所有内容(以及Microtask Queue中的每个task)之后,才能处理该事件。
在Dart中我们可以通过async关键字来声明一个异步方法,异步方法会在调用后立即返回给调用者一个Future对象,而异步方法的方法体将会在后续被执行(应该也是通过协程的方式实现)。在异步方法中可以使用await表达式挂起该异步方法中的某些步骤从而实现等待某步骤完成的目的,await表达式的表达式部分通常是一个Future类型,即在await处挂起后交出代码的执行权限直到该Future完成。在Future完成后将包含在Future内部的数据类型作为整个await表达式的返回值,接着异步方法继续从await表达式挂起点后继续执行
loadData() async {
String dataURL = "https://www.test.url";
http.Response response = await http.get(dataURL);
setState(() {
widgets = JSON.decode(response.body);
});
}
这里首先将loadData方法声明为异步方法,然后用await表达式在http.get(dataURL)处挂起等待,http是Dart提供的一个网络请求库。在请求完成时会返回一个Future对象,所以await表达式的表达式部分返回的是一个Future类型,整个await表达式返回的就是一个http.Response类型。接下来通过setState改变一个StatefulWidget的State来触发系统重新调用其build方法更新Widget。
调度任务有两种方式
选择合适的队列(一般选择event queue):
Future案例
void main(){
new Future(() => futureTask) // 异步任务的函数
.then((m) => "futueTask execute result:$m") // 任务执行完后的子任务
.then((m) => m.length) // 其中m为上个任务执行完后的返回的结果
.then((m) => printLength(m))
.whenComplete(() => whenTaskCompelete); // 当所有任务完成后的回调函数
}
int futureTask() {
return 21;
}
void printLength(int length) {
print("Text Length:$length");
}
void whenTaskCompelete() {
print("Task Complete");
}
延迟执行
new Future.delayed(const Duration(seconds: 1), () => futureTask); //延迟执行1秒,但是除非queue内部是空的,否则不止1秒
scheduleMicrotask案例
async.scheduleMicrotask(() => microtask());
void microtask(){
// doing something
}
如果要运行计算密集型任务,该怎么办?为了使您的应用程序保持响应,您应该将任务放入其自己的isolates或worker。isolate可能在单独的进程或线程中运行,具体取决于Dart实现(目前来看是在线程中运行)。
isolate是Dart对actor并发模式的实现。运行中的Dart程序由一个或多个actor组成,这些actor也就是Dart概念里面的isolate。isolate是有自己的内存和单线程控制的运行实体。isolate本身的意思是“隔离”,因为isolate之间的内存在逻辑上是隔离的。isolate中的代码是按顺序执行的,任何Dart程序的并发都是运行多个isolate的结果。因为Dart没有共享内存的并发,没有竞争的可能性所以不需要锁,也就不用担心死锁的问题。
对于计算密集型任务,通常应该使用尽可能多的isolate来提供可用的CPUs。如果它们纯粹是计算的话,任何额外的isolate都会被浪费掉。但是,如果isolate执行异步调用 - 例如执行I / O - 那么它们将不会在CPU上花费太多时间,因此拥有比CPU更多的isolate是有意义的。
如果这是一个适合您的应用程序的良好架构,您还可以使用比CPU更多的isolate。例如,您可以为每个功能使用单独的isolate,或者在需要确保不共享数据时使用。
与JVM内存模型不同的是,dart
中每个isolate
都有自己的独立的堆栈内存空间,其各自的GC不会影响到其他isolate
的。所以我们可以通过把部分占用内存空间较大且生命周期较短的对象方法其他isolate
中,这样即使另外一个isolate
GC了,并不会对我们显示UI的isolate
造成影响。
import 'dart:async';
import 'dart:isolate';
main() async {
var receivePort = new ReceivePort();
await Isolate.spawn(echo, receivePort.sendPort);
// 'echo'发送的第一个message,是它的SendPort
var sendPort = await receivePort.first;
var msg = await sendReceive(sendPort, "foo");
print('received $msg');
msg = await sendReceive(sendPort, "bar");
print('received $msg');
}
/// 新isolate的入口函数
echo(SendPort sendPort) async {
// 实例化一个ReceivePort 以接收消息
var port = new ReceivePort();
// 把它的sendPort发送给宿主isolate,以便宿主可以给它发送消息
sendPort.send(port.sendPort);
// 监听消息
await for (var msg in port) {
var data = msg[0];
SendPort replyTo = msg[1];
replyTo.send(data);
if (data == "bar") port.close();
}
}
/// 对某个port发送消息,并接收结果
Future sendReceive(SendPort port, msg) {
ReceivePort response = new ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
关于event loop:
当你要安排一个task时,请遵守以下规则: