0x00 前言
前面講了如何建立isolate,這篇文章講建立isolate的另一種方法。
0x01 使用isolates的方法
使用isolates的方法種:
- 高階API:Compute函式 (用起來方便)
- 低階API:ReceivePort
0x02 Compute函式
Compute函式對isolate的建立和底層的訊息傳遞進行了封裝,使得我們不必關係底層的實現,只需要關注功能實現。
首先我們需要:
- 一個函式:必須是頂級函式或靜態函式
- 一個引數:這個引數是上面的函式定義入參(函式沒有引數的話就沒有)
比如,還是計算斐波那契數列:
void main() async{
//呼叫compute函式,compute函式的引數就是想要在isolate裡執行的函式,和這個函式需要的引數
print( await compute(syncFibonacci, 20));
runApp(MyApp());
}
int syncFibonacci(int n){
return n < 2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1);
}
複製程式碼
執行後的結果如下:
flutter: 6765
複製程式碼
是不是很簡單,接下來看下compute
函式的原始碼,這裡的程式碼有點複雜,會把分析的新增到程式碼的註釋裡,首先介紹一個compute
函式裡用到的函式別名:
ComputeCallback<Q, R>
定義如下:
// Q R是泛型,ComputeCallback是一個有引數Q,返回值為R的函式
typedef ComputeCallback<Q, R> = R Function(Q message);
複製程式碼
正式看原始碼:
//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;
}
@immutable
class _IsolateConfiguration<Q, R> {
const _IsolateConfiguration(
this.callback,
this.message,
this.resultPort,
this.debugLabel,
this.flowId,
);
final ComputeCallback<Q, R> callback;
final Q message;
final SendPort resultPort;
final String debugLabel;
final int flowId;
R apply() => callback(message);
}
void _spawn<Q, R>(_IsolateConfiguration<Q, R> configuration) {
R result;
Timeline.timeSync(
'${configuration.debugLabel}',
() {
result = configuration.apply();
},
flow: Flow.step(configuration.flowId),
);
Timeline.timeSync(
'${configuration.debugLabel}: returning result',
() { configuration.resultPort.send(result); },
flow: Flow.step(configuration.flowId),
);
}
複製程式碼
0x03 ReceivePort
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
//一個普普通通的Flutter應用的入口
//main函式這裡有async關鍵字,是因為建立的isolate是非同步的
void main() async{
runApp(MyApp());
//asyncFibonacci函式裡會建立一個isolate,並返回執行結果
print(await asyncFibonacci(20));
}
//這裡以計算斐波那契數列為例,返回的值是Future,因為是非同步的
Future<dynamic> asyncFibonacci(int n) async{
//首先建立一個ReceivePort,為什麼要建立這個?
//因為建立isolate所需的引數,必須要有SendPort,SendPort需要ReceivePort來建立
final response = new ReceivePort();
//開始建立isolate,Isolate.spawn函式是isolate.dart裡的程式碼,_isolate是我們自己實現的函式
//_isolate是建立isolate必須要的引數。
await Isolate.spawn(_isolate,response.sendPort);
//獲取sendPort來傳送資料
final sendPort = await response.first as SendPort;
//接收訊息的ReceivePort
final answer = new ReceivePort();
//傳送資料
sendPort.send([n,answer.sendPort]);
//獲得資料並返回
return answer.first;
}
//建立isolate必須要的引數
void _isolate(SendPort initialReplyTo){
final port = new ReceivePort();
//繫結
initialReplyTo.send(port.sendPort);
//監聽
port.listen((message){
//獲取資料並解析
final data = message[0] as int;
final send = message[1] as SendPort;
//返回結果
send.send(syncFibonacci(data));
});
}
int syncFibonacci(int n){
return n < 2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1);
}
複製程式碼