Dart 異步編程之 Isolate 和事件循環
盡管 dart 是個單線程任務,但它提供 Future、Stream、后臺任務以及其他特性用于編寫現代異步程序以及響應式程序(Flutter)。本文講的是 dart 后臺任務的基礎:Isolate 和事件循環。
我們先從 Isolate 開始。
Isolates
大多數應用程序中,線程的數量都不止一個。多個線程可以互不干擾地并發執行,并共享進程的全局變量和堆的數據。
線程的訪問非常自由,它可以訪問進程內存里的所有數據,甚至包括其他線程的堆棧
《程序員的自我修養》
所有的 Dart 代碼都運行在 Isolate 中。Isolate 有自己私有的內存空間和一個基于事件循環的線程。
在其他很多語言中,比如 C++,多個線程可運行任何代碼并共享內存。但 Dart 中每個線程在其自己的 Isolate 中,有自己的內存,線程只處理事件(后面很快會詳細展開)。
大部分 Dart 應用在一個 Isolate 中運行全部代碼,也可以根據需要創建更多 Isolate。如果某個操作計算量如此之大以至于它在主 Isolate 運行中會導致掉幀,可以使用 Isolate.spawn() 或Flutter’s compute() function 方法。這些方法都會創建獨立的 Isolate 來做密集計算,讓主 Isolate 專注重建和渲染 Widget 樹。
新創建的 Isolate 有自己的事件循環和內存,原先的 Isolate (即創建新 Isolate 的那個 Isolate) 不能訪問這些內存。這種機制正是 Isolate 名字的來源:內存塊之間彼此隔離。
事實上,Isolate 之間能協作的唯一方式是消息傳遞。一個 Isolate 可以向另一個 Isolate 發送消息,接收方在其事件循環處理收到的消息。
缺少共享內存聽起來過于嚴格,尤其是你之前用過 Java 或 C++,但它給 Dart 開發者帶來一些重要的好處。
比如,Isolate 中內存分配和垃圾回收不需要鎖定。Isolate 中只有一個線程,如果它不是很忙的話,內存并不會快速變化,所以不必鎖定。這非常適合 Flutter 應用,它時常要迅速地構建和銷毀 Widget 樹。
Event loops
現在你已經了解 Isolate 了,再來看看事件循環是如何讓異步代碼變成可能的吧。
想像一下應用沿著時間線的運行過程。應用啟動,應用停止,之間發生一串串事件:磁盤 IO,用戶手勢以及類似一些事件。
應用無法預測事件何時發生、以何種順序發生,它必須在單個線程中處理所有事件并且保證不阻塞。所以應用會運行一個事件循環。它從事件隊列中取出最老的事件進行處理,然后再取下一個事件,依次進行,直到事件隊列為空。
應用一直在運行:你點擊屏幕、下載數據、觸發定時器。事件循環一直在運行,每次處理一個事件。
事件循環空閑時,線程會暫停并循環下一個事件。這時可能觸發垃圾回收器等等。Dart 為異步編程提供的所有高級 API 和語言特性,如 Future、Stream、async/await,都是基于和圍繞這個基本的循環。
比如,某個按鈕用于發起網絡請求,就像這樣:
RaisedButton( child: Text('Click me'), onPressed: () { final myFuture = http.get('https://example.com'); myFuture.then((response) { if (response.statusCode == 200) { print('Success!'); } }); }, )
1
2
3
4
5
6
7
8
9
10
11
你運行應用時,Flutter 構建按鈕并顯示到屏幕,之后應用開始等待。
應用的事件循環處于空閑,等待下一個事件。當按鈕等待點擊時,跟按鈕不相關的事件可能發生并進入到事件隊列被處理。當點擊事件發生時,最終會進入隊列。
點擊事件被取到,等待處理。Flutter 看到這個事件,它的渲染系統說 “事件坐標跟 RaisedButton 匹配”,所以 Flutter 執行 onPressed 函數。這個函數會發起網絡請求(返回一個 Future)并使用 then() 方法注冊 completion handler。
整個過程就是這樣的。事件循環處理完點擊事件后將其拋棄。
onPressed 是 RaisedButton 的一個屬性,而網絡事件為 Future 添加了一個回調,但兩者都是在相同的基本操作。它們都是在告訴 Flutter,”你好,一會兒將發生某個事件,你記得執行該事件的代碼。”
onPressed 在等待點擊,而 Future 在等待網絡數據,從 Dart 的視角,這些都是隊列中的事件。
這也正是 Dart 中異步代碼的工作方式。Future、Steam、以及 async/await,這些 API 都是你告訴 Dart 事件循環執行代碼的一種方式。
如果再來回頭看剛才的例子,你可以準確地看到它是如何為特定的事件被分解成一小塊一小塊的。
初始的 UI 構建事件
點擊事件
網絡響應事件
RaisedButton( // (1) child: Text('Click me'), onPressed: () { // (2) final myFuture = http.get('https://example.com'); myFuture.then((response) { // (3) if (response.statusCode == 200) { print('Success!'); } }); }, )
1
2
3
4
5
6
7
8
9
10
11
你習慣異步代碼之后,到處都可以看到這些模式。理解事件循環對你跟高級 API 打交道時同樣有幫助。
總結
我們簡單地了解了 Dart 中的 Isolate、事件循環以及異步編程基礎。
Android 網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。