前言
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 知識整理