轉載請聯繫: 微信號: michaelzhoujay數組
原文請訪問個人博客微信
本文主要介紹Flutter中Event Loop以及如何在Flutter中作parallel processing.網絡
First things first, everyone needs to bear in mind that Dart is Single Thread and Flutter relies on Dart.async
IMPORTANT Dart executes one operation at a time, one after the other meaning that as long as one operation is executing, it cannot be interrupted by any other Dart code.ide
跟AndroidVM相似,當你啓動一個Flutter的App,那麼就會系統就會啓動一個DartVM Flutter中的Dart VM啓動後,那麼一個新的Thread就會被建立,而且只會有一個線程,它運行在本身的Isolate中。oop
當這個Thread被建立後,DartVM會自動作如下3件事情:學習
Event Loop就像一個 infinite loop,被內部時鐘來調諧,每個tick,若是沒有其餘Dart Code在執行,就會作以下的事情(僞代碼):fetch
void eventLoop(){
while (microTaskQueue.isNotEmpty){
fetchFirstMicroTaskFromQueue();
executeThisMicroTask();
return;
}
if (eventQueue.isNotEmpty){
fetchFirstEventFromQueue();
executeThisEventRelatedCode();
}
}
複製代碼
MicroTask Queue是爲了很是短暫的asynchronously的內部操做來設計的。在其餘Dart代碼運行完畢後,且在移交給Event Queue前。ui
舉個例子,咱們常常須要在close一個resource之後,dispose掉一些handle,下面的這個例子裏,scheduleMicroTask 能夠用來作 dispose 的事情:this
MyResource myResource;
...
void closeAndRelease() {
scheduleMicroTask(_dispose);
_close();
}
void _close(){
// The code to be run synchronously
// to close the resource
...
}
void _dispose(){
// The code which has to be run
// right after the _close()
// has completed
}
複製代碼
這裏,雖然scheduleMicroTask(_dispose)
語句在_close()
語句以前,可是因爲上面說到的,「其餘Dart代碼執行完畢後」,因此_close()
會先執行,而後執行 Event loop 的 microTask。
即便你已經知道 microTask 的執行時機,並且還學習了用scheduleMicroTask
來使用 microTask,可是 microTask 也不是你經常使用的東西。就 Flutter 自己來講,整個 source code只引用了 scheduleMicroTask()
7次。
Event Queue 主要用來處理當某些事件發生後,調用哪些操做,這些事件分爲:
事實上,每當外部事件發生時,要執行的代碼都是在 Event Queue裏找到的。 只要當沒有 MicroTask 須要run了,那麼 Event Queue 就會從第一個事件開始處理
當你建立了一個future的實例,其實是作了如下幾件事情:
incomplete
Future和其餘Event同樣,會在EventQueue裏被執行。 如下的例子用來講明Future和上面的Event執行過程同樣
void main(){
print('Before the Future');
Future((){
print('Running the Future');
}).then((_){
print('Future is complete');
});
print('After the Future');
}
複製代碼
執行後,會獲得以下輸出:
Before the Future
After the Future
Running the Future
Future is complete
複製代碼
咱們來分步驟解釋一下代碼是如何執行的:
A Future is NOT executed in parallel but following the regular sequence of events, handled by the Event Loop
若是在任何一個方法的聲明部分加上 async
後綴,那麼你實際上在向dart代表:
每一個線程有本身的Isolate,你能夠用Isolate.spawn
或者是 compute
來建立一個 Isolate 每一個Isolate都有本身的Data,和Event loop Isolate之間經過消息來進行溝通
Isolate.spawn(
aFunctionToRun,
{'data' : 'Here is some data.'},
);
複製代碼
compute(
(paramas) {
/* do something */
},
{'data' : 'Here is some data.'},
);
複製代碼
當你在一個Isolate中,建立了一個新的Isolate,而後要和新的Isolate進行溝通,那麼就須要SendPort
和ReceivePort
。 爲了能溝通,兩個Isolate必需要互相知曉對方的port:
SendPort
來收/發消息,官方起的名字真的是有些讓人困惑。spawn
方法傳遞一個 ReceivePort
的實例,後續會用這個port來收/發消息,同時也會經過這個port把本地 Isolate的sendport返回找了一個例子,感覺一下
//
// The port of the new isolate
// this port will be used to further
// send messages to that isolate
//
SendPort newIsolateSendPort;
//
// Instance of the new Isolate
//
Isolate newIsolate;
//
// Method that launches a new isolate
// and proceeds with the initial
// hand-shaking
//
void callerCreateIsolate() async {
//
// Local and temporary ReceivePort to retrieve
// the new isolate's SendPort
//
ReceivePort receivePort = ReceivePort();
//
// Instantiate the new isolate
//
newIsolate = await Isolate.spawn(
callbackFunction,
receivePort.sendPort,
);
//
// Retrieve the port to be used for further
// communication
//
newIsolateSendPort = await receivePort.first;
}
//
// The entry point of the new isolate
//
static void callbackFunction(SendPort callerSendPort){
//
// Instantiate a SendPort to receive message
// from the caller
//
ReceivePort newIsolateReceivePort = ReceivePort();
//
// Provide the caller with the reference of THIS isolate's SendPort
//
callerSendPort.send(newIsolateReceivePort.sendPort);
//
// Further processing
//
}
複製代碼
兩個 Isolate 都有了各自的port,那麼它們就能夠開始互發消息了:
本地Isolate向新Isolate發消息並回收結果:
Future<String> sendReceive(String messageToBeSent) async {
//
// We create a temporary port to receive the answer
//
ReceivePort port = ReceivePort();
//
// We send the message to the Isolate, and also
// tell the isolate which port to use to provide
// any answer
//
newIsolateSendPort.send(
CrossIsolatesMessage<String>(
sender: port.sendPort,
message: messageToBeSent,
)
);
//
// Wait for the answer and return it
//
return port.first;
}
複製代碼
本地Isolate被動收消息,還記得上面的spawn
方法嗎,第一個參數是 callbackFunction
這個方法就是用來收結果的:
//
// Extension of the callback function to process incoming messages
//
static void callbackFunction(SendPort callerSendPort){
//
// Instantiate a SendPort to receive message
// from the caller
//
ReceivePort newIsolateReceivePort = ReceivePort();
//
// Provide the caller with the reference of THIS isolate's SendPort
//
callerSendPort.send(newIsolateReceivePort.sendPort);
//
// Isolate main routine that listens to incoming messages,
// processes it and provides an answer
//
newIsolateReceivePort.listen((dynamic message){
CrossIsolatesMessage incomingMessage = message as CrossIsolatesMessage;
//
// Process the message
//
String newMessage = "complemented string " + incomingMessage.message;
//
// Sends the outcome of the processing
//
incomingMessage.sender.send(newMessage);
});
}
//
// Helper class
//
class CrossIsolatesMessage<T> {
final SendPort sender;
final T message;
CrossIsolatesMessage({
@required this.sender,
this.message,
});
}
複製代碼
若是建立的Isolate再也不使用,那麼最好是能將其release掉:
void dispose(){
newIsolate?.kill(priority: Isolate.immediate);
newIsolate = null;
}
複製代碼
實際上Isolate之間的溝通是經過 「Single-Listener」 Streams 來實現的。
上面說過建立 Isolate的方式,其中 compute 適合於建立之後執行任務,並且完成任務後你不但願有任何溝通。 compute是個function:
如何挑選呢?
通常來講: