小菜剛嘗試了 Future 和 async-await 實現的簡單非同步操作,但對於耗時較長的非同步該如何處理呢?對於 Android 來說可以新開一個執行緒單獨處理,而對應的 Dart 可以用 Isolate 來處理;
Isolate
Isolate 是對 Dart 併發模式的實現,類似於 Android 中的 Thread 執行緒,但與 Thread 有本質的區別,Thread 可以實現記憶體共享,而 Isolate 不能;
所有的 Dart Code 都是在 Isolate 中執行的,程式碼只能使用同一個 Isolate 中的內容,Isolate 有自己的記憶體和事件迴圈機制;不同的 Isolate 是記憶體隔離的,因此只能通過 Port 機制傳送訊息通訊,其原理是向不同的 Isolate 佇列中執行寫任務;
Isolate 的建立
Isolate 一般通過 spawn / spawnUri 來建立物件;
Isolate.spawn()
external static Future<Isolate> spawn<T>(
void entryPoint(T message), T message,
{bool paused: false,
bool errorsAreFatal,
SendPort onExit,
SendPort onError,
@Since("2.3") String debugName});
複製程式碼
簡單瞭解原始碼,spawn 必須的引數包括需要耗時操作的 entryPoint 和 T 任意型別的 message 資料,即 Isolate 中可以傳遞任意型別的資料;
_loadIsolateDate() async {
await Isolate.spawn(_backgroundWork, ReceivePort().sendPort);
}
static _backgroundWork(SendPort sendPort) {
sendPort.send('BackgroundWork -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
}
複製程式碼
Isolate.spawnUri()
external static Future<Isolate> spawnUri(
Uri uri,
List<String> args,
var message,
{bool paused: false,
SendPort onExit,
SendPort onError,
bool errorsAreFatal,
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});
複製程式碼
簡單瞭解原始碼,spawnUri 需要三個必要引數,uri 為其他 Isolate 程式碼檔案的路徑;列表 args 為傳遞的引數列表,message 動態訊息,一般是 SendPort;
_loadIsolateDate03() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawnUri(new Uri(path: "./utils/second_isolate.dart"),
['params01, params02, params03'], receivePort.sendPort);
receivePort.listen((val) => print('listen -> 【$val】'));
}
複製程式碼
Isolate 的通訊
Isolate 可以方便的利用多核 CPU 來處理耗時操作,因記憶體不共享,需要通過 Port 進行訊息通訊;其中 Port 訊息傳遞也是非同步的;
單向通訊
Port 一般是成對出現,分別是 ReceivePort 接收埠和 SendPort 傳送埠;而 ReceivePort 也可以生成自己的 SendPort;只需要把 SendPort 傳遞給其他 Isolate 即可;
_loadIsolateDate01() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(_backgroundWork, receivePort.sendPort);
receivePort.listen((val) => print('listen -> 【$val】'));
}
static _backgroundWork(SendPort sendPort) async {
sendPort.send(
'BackgroundWork -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
Future.delayed(Duration(seconds: 3), () {
return sendPort.send(
'BackgroundWork delayed 3s -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
});
}
複製程式碼
雙向通訊
雙向通訊與單向通訊一樣,把雙方的 SendPort 相互傳遞即可;
_loadIsolateDate02() async {
ReceivePort receivePort = ReceivePort();
var sendPort;
await Isolate.spawn(_backgroundWork2, receivePort.sendPort);
receivePort.listen((val) {
if (val is SendPort) {
sendPort = val as SendPort;
print("雙向通訊建立成功");
}
print("Isolate Recevie Data -> 【$val】");
if (sendPort != null) {
sendPort.send('_loadIsolateDate02 -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
}
});
}
static _backgroundWork2(SendPort sendPort) async {
ReceivePort receivePort = new ReceivePort();
receivePort.listen((val) {
print("Background Isolate Receive Data -> 【$val]");
});
sendPort.send('BackgroundWork -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
sendPort.send(receivePort.sendPort);
Future.delayed(Duration(seconds: 2), () {
return sendPort.send('BackgroundWork delayed 2s -> currentTime -> ${DateTime.now().millisecondsSinceEpoch}');
});
Future.delayed(Duration(seconds: 5), () {
return sendPort.send(receivePort.sendPort);
});
}
複製程式碼
Isolate 的銷燬
Isolate 就像機器中的一個小空間,有自己的記憶體塊,因此在使用 Isolate 結束後應及時關閉並銷燬當前 Isolate;
isolate.kill(priority: Isolate.immediate);
複製程式碼
Tips
Q:Invalid argument(s): Isolate.spawn expects to be passed a static or top-level function
小菜剛開始建立 Isolate 時,遇到如下錯誤;
A:
需要將 Isolate.spawn 中的 message 引數方法設定成 static 方法或放置在 main() 入口;
由於篇幅和測試案例不足,小菜分為兩篇小部落格對 Isolate 進行學習,對非同步的認知還不夠深入;如有錯誤請多多指導!
來源: 阿策小和尚