前面說過 Flutter 實現異步的方式有:async、awite
、Futrue
,可是這些本質仍是 handle 隊列那套,更況且消息隊列仍是跑在 UI 線程裏的,要是你真有什麼耗時的操做放在這裏妥妥的光剩卡了。因此該開新線程仍是得開,這裏咱們來講說怎麼 new Isolate 和 Isolate 之間的通信,但後再來看看系統給咱們提供的簡便函數:compute
網絡
上文書 Isolate
之間是內存隔離的,單個 Isolate
內部是是以消息隊列的方式執行的,這是 Isolate
的特徵。語言之間概念基本趨同,只是實現和細微不一樣,可是因概相同天然就會誕生相同的需求,Dart 天然提供了 Isolate
之間通信的方式:port
端口,能夠很方便的實現 Isolate
之間的雙向通信,原理是向對方的隊列裏寫入任務異步
port
成對出現,分爲:receivePort 接受端口
和 SendPort 發送端口
,receivePort
能夠本身生成對應的 SendPort
,只要把 SendPort
傳遞給對方就能實現從 SendPort
->receivePort
的通許,固然這是單項的,雙向通信的其實也同樣,對面把本身的 SendPort
傳過來就成了async
var receivePort = ReceivePort();
var sendPort = receivePort.sendPort;
複製代碼
建立 Isolate
的 API 是:await Isolate.spawn(Function,SendPort)
,由於這是個異步操做,因此加上 await
,Function
這是個方法,是新的線程執行的核心方法,和 run
方法同樣的意思,SendPort
就是咱們要傳給對面的通信器函數
var anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
複製代碼
Isolate.listen
監聽方法咱們能夠拿到這個 Isolate
傳遞過來的數據,Isolate
之間什麼數據類型均可以傳遞,沒必要作任何標記,確定是底層幫咱們實現好了,很省事性能
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date");
});
複製代碼
await receivePort.first
能夠等待獲取第一個返回結果,可是不能和 receivePort.listen
一塊兒寫,有衝突,只能選擇一個ui
final sendPort = await receivePort.first as SendPort;
複製代碼
Isolate 關閉很直接,在 main isolate 中對其控制的 Isolate 調用 kill
方法就好了加密
void stop(){
print("kill isolate");
isolate?.kill(priority: Isolate.immediate);
isolate =null;
}
複製代碼
不廢話了,上面很簡單的,把原理一說你們都明白,下面直接看代碼:spa
isolate 代碼:
線程
import 'dart:isolate';
var anotherIsolate;
var value = "Now Thread!";
void startOtherIsolate() async {
var receivePort = ReceivePort();
anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date,value = $value");
});
}
void otherIsolateInit(SendPort sendPort) async {
value = "Other Thread!";
sendPort.send("BB");
}
複製代碼
運行代碼:
debug
import 'DartLib.dart';
void main(){
startOtherIsolate();
}
複製代碼
結果:
Isolate 1 接受消息:data = BB,value = Now Thread!
複製代碼
isolate 代碼:
import 'dart:isolate';
var anotherIsolate;
var value = "Now Thread!";
void startOtherIsolate() async {
var receivePort = ReceivePort();
var sendPort;
anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
receivePort.listen((date) {
if (date is SendPort) {
sendPort = date as SendPort;
print("雙向通信創建成功");
return;
}
print("Isolate 1 接受消息:data = $date");
sendPort.send("AA");
});
}
void otherIsolateInit(SendPort sendPort) async {
value = "Other Thread!";
var receivePort = ReceivePort();
print("Isolate 2 接受到來自 Isolate 1的port,嘗試創建同 Isolate 1的雙向通信");
receivePort.listen((date) {
print("Isolate 2 接受消息:data = $date");
});
sendPort.send(receivePort.sendPort);
for (var index = 0; index < 10; index++) {
sendPort.send("BB$index");
}
}
複製代碼
運行代碼:
import 'DartLib.dart';
void main(){
startOtherIsolate();
}
複製代碼
運行結果:
Isolate 2 接受到來自 Isolate 1的port,嘗試創建同 Isolate 1的雙向通信
雙向通信創建成功
Isolate 1 接受消息:data = BB0
Isolate 1 接受消息:data = BB1
Isolate 1 接受消息:data = BB2
Isolate 1 接受消息:data = BB3
Isolate 1 接受消息:data = BB4
Isolate 1 接受消息:data = BB5
Isolate 1 接受消息:data = BB6
Isolate 1 接受消息:data = BB7
Isolate 1 接受消息:data = BB8
Isolate 1 接受消息:data = BB9
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
Isolate 2 接受消息:data = AA
複製代碼
上面咱們本身 new 一個 Isoalte
並實現通信,多少有點麻煩,從封裝的角度看其中代碼基本是重複的,因此 Google 就提供了一個 API 來幹這事:compute
方法
compute
方法是 Flutter
提供給咱們的(記住不是 Dart),compute
內部會建立一個 Isolate
並返回計算結果,體驗上和一次性線程同樣,性能多少有些浪費,可是也有使用範圍
compute(function,value)
compute 函數接受2個參數,第一個就是新線程的核心執行方法,第二個是傳遞過新線程的參數,能夠是任何類型的數據,幾個也能夠,可是要注意,function 函數的參數設計要和 value 匹配
compute
方法在 import 'package:flutter/foundation.dart'
這個包裏面
看個例子:
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
void newTask() async {
print("開始耗時計算,當前 isolate = ${Isolate.current.toString()}");
var result = await compute(getName, "name");
print(result);
}
String getName(String name) {
print("正在獲取結果中...,當前 isolate = ${Isolate.current.toString()}");
sleep(Duration(seconds: 2));
return "Name";
}
複製代碼
運行代碼:
newTask();
複製代碼
I/flutter (24384): 開始耗時計算,當前 isolate = Instance of 'Isolate'
I/flutter (24384): 正在獲取結果中...,當前 isolate = Instance of 'Isolate'
I/flutter (24384): Name
複製代碼
compute 的源碼不難,稍微用些新就能看懂,就是 new 了一個 isolate 出來,awite 第一個數據而後返回
//compute函數 必選參數兩個,已經講過了
Future<R> compute<Q, R>(ComputeCallback<Q, R> callback, Q message, { String debugLabel }) async {
//若是是在profile模式下,debugLabel爲空的話,就取callback.toString()
profile(() { debugLabel ??= callback.toString(); });
final Flow flow = Flow.begin();
Timeline.startSync('$debugLabel: start', flow: flow);
final ReceivePort resultPort = ReceivePort();
Timeline.finishSync();
//建立isolate,這個和前面講的建立isolate的方法一致
//還有一個,這裏傳過去的參數是用_IsolateConfiguration封裝的類
final Isolate isolate = await Isolate.spawn<_IsolateConfiguration<Q, R>>(
_spawn,
_IsolateConfiguration<Q, R>(
callback,
message,
resultPort.sendPort,
debugLabel,
flow.id,
),
errorsAreFatal: true,
onExit: resultPort.sendPort,
);
final R result = await resultPort.first;
Timeline.startSync('$debugLabel: end', flow: Flow.end(flow.id));
resultPort.close();
isolate.kill();
Timeline.finishSync();
return result;
}
複製代碼
Isolate 雖好,但也有合適的使用場景,不建議濫用 Isolate,應儘量多的使用Dart中的事件循環機制去處理異步任務,這樣才能更好的發揮Dart語言的優點
那麼應該在何時使用Future,何時使用Isolate呢?一個最簡單的判斷方法是根據某些任務的平均時間來選擇:
除此以外,還有一些能夠參考的場景