前言
Dart
是谷歌开发的计算机编程语言,它被用于web
,服务器,移动应用等领域的开发,Dart
亮相于2011年,2015年5月的Dart
开发者峰会上,亮相了基于Dart
语言的移动应用开发框架Sky
,后更名为Flutter
Isolate
Dart
基于事件循环的异步模型,可以将耗时操作加入事件队列中,但是事件队列中的时间也是互相阻塞的按照顺序执行,某些耗时大的操作可以使用多线程,Dart
提供了Isolate
可以让多个任务并发执行
Isolate
可以理解为Dart中的线程,它与线程的最大区别就是多个Isolate
之间不能共享内存,这避免了线程安全问题,并且多个Isolate
之间独立垃圾回收减少了垃圾回收的性能消耗,每个Isolate
都有自己的事件循环,Isolate
之间通过消息进行通信
- 多个
Isolate
之间不能共享内存,提高了线程安全性以及垃圾回收的性能 - 每个
Isolate
都有自己独立的事件循 -
Isolate
之间通过消息进行通信
创建一个新的Isolate
大部分的Dart APP运行在一个单一的Isolate中,如果情况需要我们则需要在主Isolate中创建一个新的Isolate
spawnUri
在另一个文件中,启动一个新的main函数
external static Future<Isolate> spawnUri(
Uri uri, //新的Isolate文件路径
List<String> args, //传递给启动main函数的参数列表
var message, //其他附加参数消息
{bool paused = false, //可选参数
SendPort? onExit,
SendPort? onError,
bool errorsAreFatal = true,
bool? checked,
Map<String, String>? environment,
@Deprecated('The packages/ dir is not supported in Dart 2')
Uri? packageRoot,
Uri? packageConfig,
bool automaticPackageResolution = false,
@Since("2.3")
String? debugName});
//启动SubIsolate.dart的main函数,并且传递参数['data1',"data2","data3"],以及对象sendPort
Isolate subIsolate = await Isolate.spawnUri(new Uri(path: "SubIsolate.dart"), ['data1',"data2","data3"], sendPort)
spawn
如果不希望出现两个main函数,需要在同一个文件中创建一个新的Isolate
,spawn
可以直接将一个同文件中的函数运行在另一个Isolate
中
//启动一个新的Isolate,并执行load函数,传入参数 {"args":{"data1","data2"},"sendPort":sendPort}
Isolate subIsolate = await Isolate.spawn(load, {"args":{"data1","data2"},"sendPort":sendPort});
Isolate之间进行通信
ReceivePort
用来接收SendPort发出的信息
SendPort
用来发送信息,对应一个ReceivePort,发送的信息会被ReceivePort接收
void send(Object? message)
SendPort的函数永安里进行消息发送,消息内容是一个自定义的Object
main() {
ReceivePort receivePort = new ReceivePort();
SendPort sendPort = receivePort.sendPort;
receivePort.listen((message) {
print('receive $message');
receivePort.close(); //收到消息后关闭了ReceivePort
});
print('listener complete');
sendPort.send('msg');
sendPort.send('msg1');
}
打印结果
listener complete
receive msg
使用案例
主Isolate
会在程序开始启动时打印启动时间,耗时加载图片,打印启动完成时间,其中加载图片的操作需要在另一个Isolate
中执行,并且当其开始执行以及执行到一半时,主Isolate
需要收到消息,并且在耗时加载图片完成之后通知主Isolate
将其关闭
主Isolate
printBootTime()
loadImg()
init()
import 'dart:isolate';
main(){
printBootTime();
loadImg();
init();
}
printBootTime(){
print("启动时间"+DateTime.now().millisecondsSinceEpoch.toString());
}
loadImg() async{
print("开始加载图片");
ReceivePort receivePort = new ReceivePort();
//用于消息发送
SendPort sendPort = receivePort.sendPort;
//启动一个新的Isolate
Isolate subIsolate = await Isolate.spawnUri(new Uri(path: "SubIsolate.dart"), ['data1',"data2","data3"], sendPort);
receivePort.listen((message) {
print('主Isolate收到消息 ${message[0]}');
if(message[1] == 1){
//进行中
} else if(message[1] == 2){
//加载完毕
subIsolate.kill();
print("子Isolate关闭");
}
});
}
init(){
print("初始化参数");
}
子Isolate
import 'dart:io';
import 'dart:isolate';
main(args,SendPort mainSendPort){
print("SubIsolate.dart main args-> $args");
mainSendPort.send(['开始进行加载',0]);
sleep(new Duration(seconds: 2));
mainSendPort.send(['加载中',1]);
sleep(new Duration(seconds: 2));
mainSendPort.send(['加载完毕',2]);
}
打印结果
启动时间1624803408026
开始加载图片
初始化参数
SubIsolate.dart main args-> [data1, data2, data3]
主Isolate收到消息 开始进行加载
//两秒后
主Isolate收到消息 加载中
//两秒后
主Isolate收到消息 加载完毕
子Isolate个关闭
案例分析
- 主
Isolate
创建了一对消息发送/接收器 - 在启动子
Isolate
时候将主Isolate
的消息发送器传递 - 子
Isolate
在消息变化时,通过传入的消息发送器发送消息,从而主可以拿到子的信息
这是案例一个单向的信息传递,如果消息传递过程中,需要主向子发送消息,则子可以通过朱德发送器以消息的形式发送子的消息发送器去到主
Future与Isolate的选择
Dart
中Future
与Isolate
均可以达到异步的效果,前者是基于事件循环的异步模型,后者是通过启动了另一个Isolate
相当于是一个轻量级的线程,Isolate
虽然相比Future
更有优势,但也不能滥用
-
Isolate
开销要大于Future
,Future
是执行在单线程上的异步,当线程有空闲时候就会执行耗时操作,未来回调结果,如果不是很长的耗时操作,则使用Future
即可 - 当有很长的耗时操作,推荐使用
Isolate
,这种情况下Isolate
的大开销是值得的,并且如果在Future
上执行太长的耗时操作也会使事件队列阻塞,导致耗时操作无法依次执行 - 至于这个耗时的判断,一般几毫秒选择
Future
,数百毫秒选择Isolate
-
Future
与Isolate
的选择,以及单线程语言的优劣,是一个需要持续思考的问题
欢迎关注Mike的简书
Android 知识整理