Dart 异步编程之 Isolate 和事件循环
盡管 Dart 是個(gè)單線程任務(wù),但它提供 Future、Stream、后臺(tái)任務(wù)以及其他特性用于編寫現(xiàn)代異步程序以及響應(yīng)式程序(Flutter)。本文講的是 Dart 后臺(tái)任務(wù)的基礎(chǔ):Isolate 和事件循環(huán)。
我們先從 Isolate 開始。
Isolates
大多數(shù)應(yīng)用程序中,線程的數(shù)量都不止一個(gè)。多個(gè)線程可以互不干擾地并發(fā)執(zhí)行,并共享進(jìn)程的全局變量和堆的數(shù)據(jù)。
線程的訪問非常自由,它可以訪問進(jìn)程內(nèi)存里的所有數(shù)據(jù),甚至包括其他線程的堆棧
《程序員的自我修養(yǎng)》
所有的 Dart 代碼都運(yùn)行在 Isolate 中。Isolate 有自己私有的內(nèi)存空間和一個(gè)基于事件循環(huán)的線程。
在其他很多語言中,比如 C++,多個(gè)線程可運(yùn)行任何代碼并共享內(nèi)存。但 Dart 中每個(gè)線程在其自己的 Isolate 中,有自己的內(nèi)存,線程只處理事件(后面很快會(huì)詳細(xì)展開)。
大部分 Dart 應(yīng)用在一個(gè) Isolate 中運(yùn)行全部代碼,也可以根據(jù)需要?jiǎng)?chuàng)建更多 Isolate。如果某個(gè)操作計(jì)算量如此之大以至于它在主 Isolate 運(yùn)行中會(huì)導(dǎo)致掉幀,可以使用 Isolate.spawn() 或Flutter’s compute() function 方法。這些方法都會(huì)創(chuàng)建獨(dú)立的 Isolate 來做密集計(jì)算,讓主 Isolate 專注重建和渲染 Widget 樹。
新創(chuàng)建的 Isolate 有自己的事件循環(huán)和內(nèi)存,原先的 Isolate (即創(chuàng)建新 Isolate 的那個(gè) Isolate) 不能訪問這些內(nèi)存。這種機(jī)制正是 Isolate 名字的來源:內(nèi)存塊之間彼此隔離。
事實(shí)上,Isolate 之間能協(xié)作的唯一方式是消息傳遞。一個(gè) Isolate 可以向另一個(gè) Isolate 發(fā)送消息,接收方在其事件循環(huán)處理收到的消息。
缺少共享內(nèi)存聽起來過于嚴(yán)格,尤其是你之前用過 Java 或 C++,但它給 Dart 開發(fā)者帶來一些重要的好處。
比如,Isolate 中內(nèi)存分配和垃圾回收不需要鎖定。Isolate 中只有一個(gè)線程,如果它不是很忙的話,內(nèi)存并不會(huì)快速變化,所以不必鎖定。這非常適合 Flutter 應(yīng)用,它時(shí)常要迅速地構(gòu)建和銷毀 Widget 樹。
Event loops
現(xiàn)在你已經(jīng)了解 Isolate 了,再來看看事件循環(huán)是如何讓異步代碼變成可能的吧。
想像一下應(yīng)用沿著時(shí)間線的運(yùn)行過程。應(yīng)用啟動(dòng),應(yīng)用停止,之間發(fā)生一串串事件:磁盤 IO,用戶手勢(shì)以及類似一些事件。
應(yīng)用無法預(yù)測事件何時(shí)發(fā)生、以何種順序發(fā)生,它必須在單個(gè)線程中處理所有事件并且保證不阻塞。所以應(yīng)用會(huì)運(yùn)行一個(gè)事件循環(huán)。它從事件隊(duì)列中取出最老的事件進(jìn)行處理,然后再取下一個(gè)事件,依次進(jìn)行,直到事件隊(duì)列為空。
應(yīng)用一直在運(yùn)行:你點(diǎn)擊屏幕、下載數(shù)據(jù)、觸發(fā)定時(shí)器。事件循環(huán)一直在運(yùn)行,每次處理一個(gè)事件。
事件循環(huán)空閑時(shí),線程會(huì)暫停并循環(huán)下一個(gè)事件。這時(shí)可能觸發(fā)垃圾回收器等等。Dart 為異步編程提供的所有高級(jí) API 和語言特性,如 Future、Stream、async/await,都是基于和圍繞這個(gè)基本的循環(huán)。
比如,某個(gè)按鈕用于發(fā)起網(wǎng)絡(luò)請(qǐng)求,就像這樣:
RaisedButton(child: Text('Click me'),onPressed: () {final myFuture = http.get('https://example.com');myFuture.then((response) {if (response.statusCode == 200) {print('Success!');}});}, )你運(yùn)行應(yīng)用時(shí),Flutter 構(gòu)建按鈕并顯示到屏幕,之后應(yīng)用開始等待。
應(yīng)用的事件循環(huán)處于空閑,等待下一個(gè)事件。當(dāng)按鈕等待點(diǎn)擊時(shí),跟按鈕不相關(guān)的事件可能發(fā)生并進(jìn)入到事件隊(duì)列被處理。當(dāng)點(diǎn)擊事件發(fā)生時(shí),最終會(huì)進(jìn)入隊(duì)列。
點(diǎn)擊事件被取到,等待處理。Flutter 看到這個(gè)事件,它的渲染系統(tǒng)說 “事件坐標(biāo)跟 RaisedButton 匹配”,所以 Flutter 執(zhí)行 onPressed 函數(shù)。這個(gè)函數(shù)會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求(返回一個(gè) Future)并使用 then() 方法注冊(cè) completion handler。
整個(gè)過程就是這樣的。事件循環(huán)處理完點(diǎn)擊事件后將其拋棄。
onPressed 是 RaisedButton 的一個(gè)屬性,而網(wǎng)絡(luò)事件為 Future 添加了一個(gè)回調(diào),但兩者都是在相同的基本操作。它們都是在告訴 Flutter,”你好,一會(huì)兒將發(fā)生某個(gè)事件,你記得執(zhí)行該事件的代碼。”
onPressed 在等待點(diǎn)擊,而 Future 在等待網(wǎng)絡(luò)數(shù)據(jù),從 Dart 的視角,這些都是隊(duì)列中的事件。
這也正是 Dart 中異步代碼的工作方式。Future、Steam、以及 async/await,這些 API 都是你告訴 Dart 事件循環(huán)執(zhí)行代碼的一種方式。
如果再來回頭看剛才的例子,你可以準(zhǔn)確地看到它是如何為特定的事件被分解成一小塊一小塊的。
你習(xí)慣異步代碼之后,到處都可以看到這些模式。理解事件循環(huán)對(duì)你跟高級(jí) API 打交道時(shí)同樣有幫助。
總結(jié)
我們簡單地了解了 Dart 中的 Isolate、事件循環(huán)以及異步編程基礎(chǔ)。
總結(jié)
以上是生活随笔為你收集整理的Dart 异步编程之 Isolate 和事件循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在 Flutter 中禁用默认的 W
- 下一篇: Flutter 2 源码阅读