總目錄
Flutter開發指南之理論篇:Dart語法01(數據類型,變量,函數)
Flutter開發指南之理論篇:Dart語法02(運算符,循環,異常)
Flutter開發指南之理論篇:Dart語法03(類,泛型)
Flutter開發指南之理論篇:Dart語法04(庫,異步,正則表達式)
Flutter開發指南之理論篇:Dart語法05(單線程模型,事件循環模型,Isolate)
Flutter開發指南之理論篇:Flutter基礎01(架構,設計思想)
?Dart是一門面向對象語言,它針對web 和移動設備開發進行了優化,主要特點為:
- 一切皆對象!無論是數字,函數還是null,所有對象繼承自Object類;
- 聲明一個變量時可以不指定具體類型,Dart可以自動推斷類型;
- Dart支持頂層函數,函數是一等對象,且函數可作為參數傳遞;
- Dart使用
_
開頭表示私有屬性,沒有關鍵字public
,protected
和private
;
1. 單線程模型
?眾所周知,在Java中使用多線程
來處理并發任務,適量并合適地使用多線程,能夠極大地提高資源的利用率和程序運行效率,但是缺點也比較明顯,比如過度開啟線程會帶來額外的資源和性能消耗
或多線程共享內存容易出現死鎖
等。實際上,在APP的使用過程中,多數處理空閑狀態,并不需要進行密集或高并發的處理,因此從某些意義上來說,多線程顯得有點多余。正是因為如此,Dart作為一種新的語言,通過引單線程模型
很好地處理了并發任務對多線程的依賴。
1.1 單線程模型
?Dart是一種單線程語言,因此Dart程序沒有主線程和子線程之分,而在Dart中線程并不是指Thread
,而是指Isolate
。因為Dart沒有線程的概念,只有Isolate
,每個Isolate
都是隔離的,并不會共享內存。所有的Dart代碼都是在Isolate
中運行,它就像機器上的一個小空間,具有自己的私有內存塊和一個運行著事件循環模型
的單線程。也就是說,一旦某個Dart函數開始執行,它將執行到這個函數的結束而不被其他Dart代碼打斷,這就是單線程的特性。
?默認情況下,Dart程序只有一個Isolate
(未自己創建的情況下),而這個Isolate
就是Main Isolate
。也就是說,一個Dart程序是從Main Isolate
的main函數開始的,而在main函數結束后,Main isolate
線程開始一個一個處理事件循環模型隊列中的每一事件(Event)
。上圖描述的就是Main Isolate
的消息循環模型。
1.2 事件循環模型
?也許你會問,既然Dart是一種單線程語言,那么是不是就意味著Dart無法并發處理異步任務了?此言差矣。前面說到,所有的Dart程序都在Isolate
中運行,每個Isolate
擁有自己的私有內存塊和一個事件循環模型
,其中,事件循環模型
就是用來處理各種事件,比如點輸入/輸出,點擊,定時器以及異步任務等。下圖描述了一個Isolate
的事件循環模型
的整個流程:
?從上圖可知,Dart事件循環機制由一個消息循環(event looper)
和兩個消息隊列
構成,其中,兩個消息隊列是指事件隊列(event queue)
和微任務隊列(Microtask queue)
。該機制運行原理為:
- 首先,Dart程序從main函數開始運行,待main函數執行完畢后,event looper開始工作;
- 然后,event looper優先遍歷執行Microtask隊列所有事件,直到Microtask隊列為空;
- 接著,event looper才遍歷執行Event隊列中的所有事件,直到Event隊列為空;
- 最后,視情況退出循環。
?為了進一步理解,我們解釋下上述三個概念:
(1)消息循環(Event Looper
)
?顧名思義,消息循環就是指一個永不停歇且不能阻塞的循環,它將不停的嘗試從微任務隊列
和事件隊列
中獲取事件(event)進行處理,而這些Event包括了用戶輸入,點擊,Timer,文件IO等。
(2)事件隊列(Event queue
)
?該隊列的事件來源于外部事件
和Future
,其中,外部事件
主要包括I/O,手勢,繪制,計時器和isolate相互通信的message等,而Future
主要是指用戶自定義的異步任務,通過創建Future類實例來向事件隊列添加事件。需要注意的是,當Event looper正在處理Microtask Queue
時,Event queue
會被阻塞,此時APP將無法進行UI繪制,響應用戶輸入和I/O等事件。下列示例演示了向Event queue
中添加一個異步任務事件:
main(List<String> args) {
print('main start...')
var futureInstance = Future<String>(() => "12345");
futureInstance.then((res) {
print(res);
}).catchError((err) {
print(err);
});
print('main end...')
}
// 打印結果:
// main start...
// main end...
// 12345
(3)微任務隊列(Microtask queue
)
?該隊列的事件來源與當前isolate的內部或通過scheduleMicrotask
函數創建,Microtask一般用于非常短的內部異步動作,并且任務量非常少,如果微任務非常多,就會造成Event queue
排不上隊,會阻塞Event queue
的執行造成應用ANR,因為Microtask queue
的優先級高于Event queue
。因此,大多數情況下的任務優先考慮使用Event queue
,不到萬不得已不要使用Microtask queue
。下列示例演示了兩個事件隊列執行情況:
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 2'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 3'));
new Future(() => print('future #3 of 3'));
scheduleMicrotask(() => print('microtask #2 of 2'));
print('main #2 of 2');
}
// 執行結果:
// main #1 of 2
// main #2 of 2
// microtask #1 of 2
// microtask #2 of 2
// future #2 of 3
// future #3 of 3
// future #1 (delayed)
2. Isolate
?大多數計算機中,甚至在移動平臺上,都在使用多核CPU。 為了有效利用多核性能,開發者一般使用共享內存數據來保證多線程的正確執行。 然而多線程共享數據通常會導致很多潛在的問題,并導致代碼運行出錯。Dart作為一種新語言,為了緩解上述問題,提出了Isolate(隔離區)
的概念,即Dart沒有線程的概念,只有Isolate
,所有的Dart代碼都是在Isolate
中運行,它就像是機器上的一個小空間,具有自己的私有內存堆和一個運行著Event Looper的單個線程。
?通常,一個Dart應用對應著一個Main Isolate
,且應用的入口即為該Isolate的main函數。當然,我們也可以創建其它的Isolate
,由于Isolate
的內存堆是私有的,因此這些Isolate
的內存都不會被其它Isolate
訪問。假如不同的Isolate
需要通信(單向/雙向),就只能通過向對方的事件循環隊列里寫入任務,并且它們之間的通訊方式是通過port(端口)
實現的,其中,Port又分為receivePort(接收端口)
和sendPort(發送端口)
,它們是成對出現的。Isolate之間通信過程:
- 首先,當前
Isolate
創建一個ReceivePort
對象,并獲得對應的SendPort
對象;
var receivePort = ReceivePort();
var sendPort = receivePort.sendPort;
- 其次,創建一個新的
Isolate
,并實現新Isolate
要執行的異步任務,同時,將當前Isolate的SendPort對象傳遞給新的Isolate,以便新Isolate使用這個SendPort對象向原來的Isolate發送事件;
// 調用Isolate.spawn創建一個新的Isolate
// 這是一個異步操作,因此使用await等待執行完畢
var anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
// 新Isolate要執行的異步任務
// 即調用當前Isolate的sendPort向其receivePort發送消息
void otherIsolateInit(SendPort sendPort) async {
value = "Other Thread!";
sendPort.send("BB");
}
- 第三,調用當前Isolate#receivePort的listen方法監聽新的Isolate傳遞過來的數據。Isolate之間什么數據類型都可以傳遞,不必做任何標記。
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date");
});
- 最后,消息傳遞完畢,關閉新創建的Isolate。
anotherIsolate?.kill(priority: Isolate.immediate);
anotherIsolate =null;
示例代碼如下(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");
}
// 在Main Isolate創建一個新的Isolate
// 并使用Main Isolate的ReceiverPort接收新Isolate傳遞過來的數據
import 'DartLib.dart';
void main(){
startOtherIsolate();
}
?執行結果:
Isolate 1 接受消息:data = BB,value = Now Thread!
3. 參考文獻
1. Dart asynchronous programming: Isolates and event loops
2. Futures - Isolates - Event Loop
3. Flutter 真異步