??????小菜前段時(shí)間簡單研究了一下 Dart 單線程實(shí)現(xiàn)異步的操作,今天繼續(xù)學(xué)習(xí) Dart 的事件機(jī)制的任務(wù)調(diào)度乌妙;
任務(wù)調(diào)度
??????Dart 是單線程的,一個(gè) Flutter 程序由一個(gè)或多個(gè) isolate 組成建钥,默認(rèn)的執(zhí)行方法均是在 main isolate 中藤韵;一個(gè) isolate 中包含一個(gè) Event Loop 和一個(gè) Task Queue,而 Task Queue 包含 MicroTask Queue 微事件隊(duì)列和 Event Queue 事件隊(duì)列兩種熊经;
??????Dart 的事件機(jī)制是根據(jù)任務(wù)調(diào)度優(yōu)先級來實(shí)現(xiàn)的泽艘;其中將任務(wù)添加到 MicroTask Queue 微事件隊(duì)列的方式有 scheduleMicrotask() 和 Future.microtask() 兩種;而將任務(wù)添加到 Event Queue 事件隊(duì)列一般通過 Future 的相關(guān)構(gòu)造方法實(shí)現(xiàn)奈搜;
??????MicroTask Queue 微事件隊(duì)列的執(zhí)行優(yōu)先級高于 Event Queue 事件隊(duì)列悉盆,而小菜簡單理解,兩種均類似于 Android 線程中的異步操作馋吗,只是 MicroTask Queue 微事件隊(duì)列優(yōu)先級相對更高焕盟;
Dart 的事件執(zhí)行順序如圖所示;
- 啟動(dòng) app 后優(yōu)先執(zhí)行 main() 方法中的同步方法宏粤;
- 查看 MicroTask Queue 是否為空脚翘,若不為空,優(yōu)先循環(huán)執(zhí)行 MicroTask Queue 中的 MicroTask绍哎,直到隊(duì)列為空来农;
- 若 MicroTask Queue 隊(duì)列為空則查看 Event Queue 事件隊(duì)列,若不為空崇堰,則循環(huán)執(zhí)行 Event Queue 中的 Event 事件沃于,直到隊(duì)列為空;
- 等兩個(gè)隊(duì)列的任務(wù)均執(zhí)行完成后結(jié)束海诲;
??????Tips: 當(dāng)任務(wù)隊(duì)列執(zhí)行 MicroTask Queue 微事件隊(duì)列時(shí)繁莹,Event Queue 事件隊(duì)列被卡住,即應(yīng)用無法繪制圖形特幔,處理鼠標(biāo)點(diǎn)擊事件咨演,無法對 I/O 事件做出反應(yīng)等;
案例嘗試
??????每個(gè) isolate 有各自的內(nèi)存塊和 Event Loop 是互相隔離的蚯斯,小菜只嘗試單個(gè) isolate 中的微事件隊(duì)列和事件隊(duì)列的執(zhí)行順序薄风;
- 小菜優(yōu)先嘗試最基本的 Future 構(gòu)造函數(shù)和 MicroTask 的執(zhí)行順序饵较;
_taskQueue01() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
??????根據(jù)上述執(zhí)行順序,首先執(zhí)行 main() 中同步的日志輸出 Click start -> end遭赂,之后優(yōu)先執(zhí)行 MicroTask 微事件 scheduleMicrotask A -> C循诉;再之后執(zhí)行添加到 EventTask 事件隊(duì)列的 Future() 構(gòu)造函數(shù) Future A -> C;最后執(zhí)行因延遲 2s 加入事件隊(duì)列的 Future B嵌牺;
- 小菜在【案例一】的基礎(chǔ)上嘗試添加 Future.microtask() 構(gòu)造函數(shù)打洼;
_taskQueue02() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
??????簡單了解 Future.microtask() 函數(shù)源碼,其實(shí)就是封裝了一個(gè) scheduleMicrotask() 微事件逆粹,所以微事件隊(duì)列執(zhí)行順序?yàn)?scheduleMicrotask A -> B -> C募疮;
- 小菜在【案例二】的基礎(chǔ)上嘗試添加 Future.sync() 構(gòu)造函數(shù);
_taskQueue03() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.sync(() => print('TaskQueue -> Future.sync D'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
??????Future.sync() 為同步方法僻弹,會(huì)立即執(zhí)行阿浓,因此是在 main() 點(diǎn)擊同步日志 start 和 end 之間的;
- 小菜嘗試添加 Future.then() 級聯(lián)方法蹋绽;
_taskQueue04() {
Future(() => print('TaskQueue -> Future A'))
.then((_) => print('TaskQueue -> Future A -> then()01'))
.then((_) => print('TaskQueue -> Future A -> then()02'));
Future.sync(() => print('TaskQueue -> Future.sync D'))
.then((_) => print('TaskQueue -> Future.sync D -> then()01'))
.then((_) => print('TaskQueue -> Future.sync D -> then()02'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()01'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()02'));
Future(() => print('TaskQueue -> Future C'))
.then((_) => print('TaskQueue -> Future C -> then()01'))
.then((_) => print('TaskQueue -> Future C -> then()02'));
}
??????Future.then() 級聯(lián)函數(shù)是按照順序執(zhí)行的芭毙,執(zhí)行完第一個(gè) then() 函數(shù)后才會(huì)執(zhí)行第二個(gè) then();且小菜理解為 then() 函數(shù)會(huì)在 Future() 執(zhí)行之后立即執(zhí)行卸耘,可以看作是存放在微事件隊(duì)列中退敦;因此整體的執(zhí)行順序是 sync()-> sync().then() -> microtask() -> microtask().then() -> Future() -> Future().then() -> delayed() -> delayed().then();
- 小菜在【案例三 + 四】的基礎(chǔ)上添加 Future.whenComplete() 方法蚣抗;
_taskQueue05() {
Future(() => print('TaskQueue -> Future A'))
.then((_) => print('TaskQueue -> Future A -> then()01'))
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.sync(() => print('TaskQueue -> Future.sync D'))
.then((_) => print('TaskQueue -> Future.sync D -> then()01'))
.then((_) => print('TaskQueue -> Future.sync D -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.sync D -> whenComplete()'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()01'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.microtask B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) => print('TaskQueue -> Future C -> then()01'))
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
??????Future.whenComplete() 是在 Future() 及 then() 函數(shù)執(zhí)行完成之后立即執(zhí)行侈百;小菜也簡單看作是存放在微事件隊(duì)列中;依照小菜的測試案例來看翰铡,sync 執(zhí)行同步任務(wù)完成后钝域,會(huì)循環(huán)遍歷微事件隊(duì)列,其中 scheduleMicrotask A 是在 sync() 函數(shù)之前加入 MicroTask Queue 中锭魔,因此是先執(zhí)行 scheduleMicrotask A 再執(zhí)行 sync() 之后的 then() 和 whenComplete() 方法例证;
- 小菜嘗試在 then() 函數(shù)中執(zhí)行新的事件隊(duì)列;
_taskQueue06() {
Future(() => print('TaskQueue -> Future A'))
.then((_) {
print('TaskQueue -> Future A -> then()01');
return Future.delayed(Duration(seconds: 1), () => print('TaskQueue -> Future.delayed D'));
})
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) {
print('TaskQueue -> Future C -> then()01');
return scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
})
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
}
????a. 首先執(zhí)行 MicroTask Queue 微事件隊(duì)列中的 scheduleMicrotask A -> B迷捧;
????b. 之后以 FIFO 先進(jìn)先出的順序執(zhí)行 EventTask Queue 事件隊(duì)列中 Task织咧;先執(zhí)行 Future A,之后是 A 中 then()01漠秋,此時(shí)小菜設(shè)置了一個(gè) 1s 的延遲 Future.delayed D烦感,因?yàn)?then() 的級聯(lián)函數(shù)是需要等前面的 then() 函數(shù)執(zhí)行完成后才能執(zhí)行;因此優(yōu)先執(zhí)行 EventTask Queue 中的 Future C膛堤;
????c. 執(zhí)行完 Future C 之后執(zhí)行 then()01,此時(shí)小菜設(shè)置了一個(gè) MicroTask晌该,依照小菜的理解肥荔,then() / whenComplete() 均存放在 MicroTask Queue 中绿渣,因此新設(shè)置的 MicroTask 會(huì)放置在 MicroTask Queue 末尾,等 then()02 和 whenComplete() 再執(zhí)行
????d. 此時(shí) EventTask Queue 事件隊(duì)列中已執(zhí)行完畢燕耿,在 1s 后添加了新的 Future.delayed D 并執(zhí)行中符;
????e. 待 Future.delayed D 執(zhí)行結(jié)束或,Future A 的第二個(gè)級聯(lián) then() 函數(shù)和 whenComplete() 開始執(zhí)行誉帅;
????f. 最后執(zhí)行延遲最久的 2s 的 Future.delayed B 及其 then() / whenComplete() 函數(shù)淀散;
- 小菜在測試過程中,與【案例六】代碼幾乎一致蚜锨,只是在 Future.then() 中調(diào)用 Future() 時(shí)少了 return档插;
_taskQueue07() {
Future(() => print('TaskQueue -> Future A'))
.then((_) {
print('TaskQueue -> Future A -> then()01');
Future.delayed(Duration(seconds: 1), () => print('TaskQueue -> Future.delayed D'));
})
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) {
print('TaskQueue -> Future C -> then()01');
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
})
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
}
??????小菜測試發(fā)現(xiàn),雖然只是少了兩個(gè) return 但是執(zhí)行順序卻變化很大亚再;
????a. 首先執(zhí)行微事件隊(duì)列中的 scheduleMicrotask A -> B
????b. 之后執(zhí)行 EventTask Queue 中的 Future A郭膛,執(zhí)行第一個(gè) print(then()01) 之后,小菜設(shè)置了 Future.delayed D氛悬,因?yàn)闊o需返回则剃,所以將 Future.delayed D 事件隊(duì)列末位后第一個(gè) then() 函數(shù)已完成執(zhí)行,此時(shí)可以執(zhí)行第二個(gè) then()02 和 whenComplete() 函數(shù)如捅;
????c. 繼續(xù)執(zhí)行 EventTask Queue 中的 Future C棍现,執(zhí)行第一個(gè) print(then()01) 之后,小菜設(shè)置了 scheduleMicrotask() C镜遣,無需 return己肮,將 scheduleMicrotask() C 添加到微事件隊(duì)列末位,此時(shí)第一個(gè) then() 函數(shù)以完成執(zhí)行烈涮,執(zhí)行微事件隊(duì)列中的第二個(gè) then()02 和 whenComplete() 函數(shù)朴肺,再之后執(zhí)行微事件隊(duì)列中最后的 scheduleMicrotask() C;
????d. 之后執(zhí)行事件隊(duì)列末位的 1s 的 Future.delayed D坚洽,此時(shí)事件隊(duì)列已為空戈稿;
????e. 最后執(zhí)行 2s 添加到事件隊(duì)列中的 Future.delayed B 及其 then() / whenComplete() 函數(shù);
匯總小結(jié)
- Dart 的任務(wù)調(diào)度模型是單線程輪詢讶舰,不是基于時(shí)間調(diào)度的鞍盗;我們可以知道各個(gè)事件的調(diào)度順序,但無法明確得知各個(gè)事件調(diào)度的時(shí)間跳昼;例如:延遲 2s 并非一定是 2s 后執(zhí)行般甲,需要等之前的任務(wù)調(diào)度結(jié)束后才會(huì)執(zhí)行;
- 注意級聯(lián)函數(shù)中的新的任務(wù)調(diào)度是否需要返回結(jié)果后再繼續(xù)鹅颊;
- 實(shí)際中盡量在 EventTask 中執(zhí)行耗時(shí)操作敷存,減少在 MicroTask 中執(zhí)行;若任務(wù)耗時(shí)時(shí)間較長可以嘗試用 isolate 開啟新的異步線程執(zhí)行;
??????Task Queue 案例嘗試
??????小菜對任務(wù)調(diào)度的理解還不夠深入锚烦,如有錯(cuò)誤請多多指導(dǎo)觅闽!
來源: 阿策小和尚