Dart
中的事件循环是单线程的,在流畅性与安全性体验较好,核心分为主线程、微任务、宏任务。主线程主要包括业务处理、网络IO、本地文件IO、异步等事件。dart的单线程中有两个事件队列,一个是微任务队列、一个是事件队列。git
微任务队列包含有 Dart 内部的微任务,主要是经过 scheduleMicrotask 来调度。github
事件队列包含外部事件,例如 I/O 、 Timer ,绘制事件等等。web
若是你的代码具备依赖型,最好是显式的。这有助于开发人员理解您的代码并使您的代码更加健壮。安全
想给事件队列中链式添加任务的错误操做示范:bash
future.then(...set an important variable...);
Timer.run(() {...use the important variable...});
复制代码
正确的是:markdown
future.then(...set an important variable...)
.then((_) {...use the important variable...});
复制代码
在Dart
单线程中,他们的结果都是一致的,可是理解起来难易程度不是一致的。网络
当你想稍后在任务中执行你的代码,可使用Future
类,它将把任务添加到事件队列末尾,或者使用顶级函数scheduleMicrotask
把任务添加到微任务队列的末尾。多线程
为了更好的使用then
,或者在发生错误时也须要执行的话,那么请在whenComplate()
代替then
.并发
可使用Timer
或者Future
均可以异步
您也可使用Timer计划任务,可是若是任务中发生任何未捕获的异常,则应用程序将退出。相反,咱们建议使用Future,它创建在Timer之上,并增长了诸如检测任务完成和对错误进行响应的功能。
添加代码到事件队列
new Future(() { // ...code goes here... }); 复制代码
你可使用then
或者whenComplate
执行后边的任务,看下这个例子
new Future(() => 21) .then((v) => v*2) .then((v) => print(v)); 复制代码
若是你想稍后再执行的话,请使用Future.delayed()
:
new Future.delayed(const Duration(seconds:1), () { // ...code goes here... }); 复制代码
了解了基本的用法以后,那么如何把他们搭配使用呢?
有了单线程和队列,那必然有对应的循环,这样子才能执行不一样的队列任务和处理事件,那么咱们看下 循环。
main
函数,并产生相应的微任务和事件队列验证一下上边的运行原理,咱们看下下边的代码:
void main() { test(); } /// 微任务 /// 定时器 void test() async { print('start'); scheduleMicrotask(() { print('Microtask 1'); }); Future.delayed(Duration(seconds: 0)).then((value) { print('Future 1'); }); Timer.run(() { print('Timer 1'); }); print('end'); } 复制代码
运行过程以下:
main
函数,打印start
scheduleMicrotask
微任务,添加任务到微任务队列中Future
事件,给事件队列添加任务timer
事件,给事件队列添加任务end
Microtask 1
Future
任务,执行打印Future 1
Timer 1
任务,执行打印Timer1
输出
start
end
Microtask 1
Future 1
Timer 1
复制代码
看下面这个例子证实Timer
和Future
是一个类型的事件。
/// 微任务 /// 定时器 void test2() async { print('start'); scheduleMicrotask(() { print('Microtask 1'); }); Timer.run(() { print('Timer 1'); }); Future.delayed(Duration(seconds: 0)).then((value) { print('Future 1'); }); print('end'); } 复制代码
输出
start
end
Microtask 1
Timer 1
Future 1
复制代码
上面的test
和test2
中Timer
和Future
位置调换了一下,也就是添加事件任务前后顺序颠倒了一下,在执行的时候也颠倒了一下。咱们再看Future
源码:
factory Future.delayed(Duration duration, [FutureOr<T> computation()]) { _Future<T> result = new _Future<T>(); new Timer(duration, () { if (computation == null) { result._complete(null); } else { try { result._complete(computation()); } catch (e, s) { _completeWithErrorCallback(result, e, s); } } }); return result; } 复制代码
Future.delayed
源码本质就是将任务添加到Timer
中,在指定时间后运行该任务。
微任务队列优先级高于事件队列,因此每次执行任务首先判断是否存在未执行的微任务。
事件队列执行完,有可能有新的微任务被添加到队列中,因此还须要扫描微任务队列一次。 看下面的例子:
void test3() async { print('start'); //1 scheduleMicrotask(() {//2 print('Microtask 1');//5 }); Timer.run(() {//3 print('Timer 1');//6 Timer.run(() {//9 print('Timer 1 Microtask 2 ');//10 }); scheduleMicrotask(() {//7 print('Microtask 2');//8 }); }); print('end');//4 } 复制代码
执行顺序:
start
Timer 1
到事件队列end
任务Microtask 1
,当即执行Timer 1
任务并执行,添加事件任务Timer 1 Microtask 2
,添加微任务Microtask 2
到微任务队列Microtask 2
,并执行。Timer 1 Microtask 2
并执行。结果输出
start
end
Microtask 1
Timer 1
Microtask 2
Timer 1 Microtask 2
复制代码
根据前边的了解,执行完微任务再执行事件任务,当某个事件处理时间须要很长,则后边的任务则会一直处于等待状态。下边咱们看一个例子,当任务足够都,仍是须要必定时间去处理的。
void test4() async { print('start ${DateTime.now()}'); for(int i =0;i < 99999;i++){ scheduleMicrotask(() { print('Microtask 1'); }); } Timer.run(() { print('Timer 1 ${DateTime.now()}'); }); print('end ${DateTime.now()}'); } 复制代码
输出:
start 2020-07-28 17:44:11.561886
end 2020-07-28 17:44:11.593989
...
Microtask 1
.....
Timer 1 2020-07-28 17:44:11.893093
复制代码
能够看出这些任务执行完成耗时基本达到了0.33
秒。
当一个线程出现处理任务不够了,那么就须要在开启一个线程了。
上边的 dart
进入main函数是单线程的,在Dart中,多线程叫作Isolates
线程,每一个Isolates线程不共享内存,经过消息机制通讯。
咱们看个例子,利用Dart
的Isolates
实现多线程。
void test5()async{ final rece = ReceivePort(); isolate = await Isolate.spawn(sendPort, rece.sendPort); rece.listen((data){ print('收到了 ${data} ,name:$name'); }); } void sendPort(SendPort sendPort){ sendPort.send('发送消息'); } Isolate isolate; String name='fgyong'; void main() { test5(); } 复制代码
输出
收到了 发送消息 ,name:fgyong
复制代码
多线程相互沟通怎么处理?
建立线程以后子线程须要发送主线程一个端口和消息,主线程记录该端口,下次和子线程通信使用该端口便可。
具体代码以下:
/// 新线程执行新的任务 并监听 Isolate isolate; Isolate isolate2; void createTask() async { ReceivePort receivePort = ReceivePort(); isolate = await Isolate.spawn(sendP1, receivePort.sendPort); receivePort.listen((data) { print(data); if (data is List) { SendPort subSencPort = (data as List)[1]; String msg = (data as List)[0]; print('$msg 在主线程收到'); if (msg == 'close') { receivePort.close(); } else if (msg == 'task') { taskMain(); } subSencPort.send(['主线程发出']); } }); } void sendP1(SendPort sendPort) async { ReceivePort receivePort = new ReceivePort(); receivePort.listen((data) async { print(data); if (data is List) { String msg = (data as List)[0]; print('$msg 在子线程收到'); if (msg == 'close') { receivePort.close(); } else if (msg == 'task') { var m = await task(); sendPort.send(['$m', receivePort.sendPort]); } } }); sendPort.send(['子线程线程发出', receivePort.sendPort]); } Future<String> task() async { print('子线程执行task'); for (var i = 0; i < 99999999; i++) {} return 'task 完成'; } void taskMain() { print('主线程执行task'); } 复制代码
输出:
[子线程线程发出, SendPort]
子线程线程发出 在主线程收到
[主线程发出]
主线程发出 在子线程收到
复制代码
更多子线程与主线程交互请上代码库查看
假设一个项目,须要 2 个团队去完成,团队中包含多项任务。能够分为 2 个高优先级任务(高优先级的其中,会产生2个任务,一个是紧急一个是不紧急),和 2 个非高优先级任务(非高优先级的其中,会产生有 2 个任务,一个是紧急一个是不紧急)。其中还有一个是必须依赖其余团队去作的,由于本团队没有那方面的资源,第三方也会产生一个高优先级任务和一个低优先级任务。
根据紧急任务做为微任务,非紧急任务做为事件任务来安排,第三方是新开线程
主任务 | 高优先级(微任务) | 低优先级(事件任务) | 第三方(Isolate) |
---|---|---|---|
H1 | H1-1 | L1-2 | 否 |
H2 | H2-1 | L2-2 | 否 |
L3 | H3-1 | L3-2 | 否 |
L4 | H4-1 | L4-2 | 否 |
I5 | IH5-1 | I5-2 | 是 |
void test6() { createTask();//建立线程 scheduleMicrotask(() {//第一个微任务 print('H1'); scheduleMicrotask(() {//第一个紧急任务 print('H1-1'); }); Timer.run(() {//第一个非紧急任务 print('L1-2'); }); }); scheduleMicrotask(() {// 第二个高优先级任务 print('H2'); scheduleMicrotask(() {//第二个紧急任务 print('H2-1'); }); Timer.run(() {//第二个非紧急任务 print('L2-2'); }); }); Timer.run(() {// 第一个低优先级任务 print('L3'); scheduleMicrotask(() {//第三个紧急任务 print('H3-1'); }); Timer.run(() {//第三个非紧急任务 print('L3-2'); }); }); Timer.run(() {// 第二个低优先级任务 print('L4'); scheduleMicrotask(() {//第四个紧急任务 print('H4-1'); }); Timer.run(() {//第四个非紧急任务 print('L4-2'); }); }); } /// 新线程执行新的任务 并监听 Isolate isolate; void createTask() async { ReceivePort receivePort = ReceivePort(); isolate = await Isolate.spawn(sendPort, receivePort.sendPort); receivePort.listen((data) { print(data); }); } /// 新线程执行任务 void sendPort(SendPort sendPort) { scheduleMicrotask(() { print('IH5-1'); }); Timer.run(() { print('IL5-2'); }); sendPort.send('第三方执行任务结束'); } 复制代码
运行结果
H1
H2
H1-1
H2-1
L3
H3-1
L4
H4-1
L1-2
L2-2
L3-2
L4-2
IH5-1
IL5-2
第三方执行任务结束
复制代码
能够看到H
开头的为高优先级,L
开头为低优先级,基本高优先级运行都在低优先级以前,符合预期。
因为建立线程须要事件,其余任务均为耗时过短,那么咱们从新作一个耗时事件长的任务便可。
createTask();//建立线程 for (var i = 0; i < 9999999999; i++) { } ... 复制代码
输出:
IH5-1
IL5-2
H1
H2
H1-1
H2-1
第三方执行任务结束
L3
H3-1
L4
H4-1
L1-2
L2-2
L3-2
L4-2
复制代码
由于这个事件属于低优先级,而H
开头的都是高优先级任务。
在L3
上边。而不是在最下边,必定在低优先级队列的第一个吗 ?
因为开始的耗时操做事件太长,致使全部任务执行前,第三方任务已经执行完成,因此 第三方执行任务结束 是第一个添加到低优先级任务队列的,因此在低优先级队列第一个执行。
当耗时操做比较少时,则 第三方执行任务结束 添加顺序则不肯定。
Dart
中异步和多线程是分开的,异步只是事件循环中的多事件轮训的结果,而多线程能够理解为真正的并发(多个线程同时作事)。在单个线程中,又分为微任务和其余事件队列,微任务队列优先级高于其余事件队列。