Dart異步編程:隔離和事件循環

Dart異步編程:隔離和事件循環

儘管Dart是一種單線程語言,但它提供了對futures、streams、background工作以及所有其他需要用現代、異步和(對於Flutter而言)反應式編程的內容的支持。本文介紹了Dart對background工作的支持的基礎:隔離(isolates )和事件循環(event loops)。

如果您喜歡通過觀看或聆聽來學習,則以下視頻介紹了本文中的所有內容,該視頻屬於Flutter in Focus視頻系列Dart中的異步編程的一部分:

https://youtu.be/vl_AaCgudcY

還在這裏嗎?我們來談談isolates(隔離)

Isolates(隔離)

隔離是所有Dart代碼運行的地方。它就像機器上的一個小空間,有它自己的私有內存塊和一個運行事件循環的線程。

隔離具有自己的內存和運行事件循環的單個執行線程。

在許多其他語言(例如C ++)中,您可以讓多個線程共享相同的內存並運行所需的任何代碼。 但是,在Dart中,每個線程都處於其自己的隔離區中,並擁有自己的內存,並且該線程僅處理事件(稍後會詳細介紹)。

許多Dart應用程序都在單個隔離中運行所有代碼,但是如果需要,您可以擁有多個。 如果要執行的計算量如此之大,如果它在主隔離中運行,可能會導致丟幀,那麼您可以使用Isolate.spawn()或Flutter的compute()函數。 這兩個函數都創建了一個單獨的隔離程序來進行數字運算,從而使您的主要隔離程序可以自由地(例如,重建和呈現小部件樹)。

兩個隔離,每個隔離都有自己的內存和執行線程。

新隔離區擁有自己的事件循環和內存,即使原始隔離區是該新隔離區的父級,也不允許其訪問。 這就是isolate的名稱的來源:這些小空間彼此隔離。

事實上,隔離能夠協同工作的唯一方式是來回傳遞消息。一個隔離程序向另一個隔離程序發送消息,接收隔離程序使用其事件循環處理該消息。

隔離區可以將消息發送到其他隔離區。

共享內存的缺乏可能聽起來有點嚴格,特別是如果您來自Java或c++這樣的語言,但它對Dart編碼人員有一些關鍵的好處。

例如,一個隔離中的內存分配和垃圾收集不需要鎖定。只有一個線程,所以如果它不忙,您就知道內存沒有發生變化。這對於Flutter應用來說非常有效,因爲它有時需要快速構建和拆除一堆小部件。

事件循環

現在,您已經對隔離有了基本的介紹,讓我們深入研究真正使異步代碼成爲可能的東西:事件循環。

想象一下應用程序的壽命在時間軸上延伸。 該應用程序啓動,該應用程序停止,並且在一系列事件之間發生–來自磁盤的I / O,來自用戶的手指敲擊……各種各樣的東西。

您的應用程序無法預測這些事件何時或以何種順序發生,它必須用一個永不會阻塞的線程來處理所有這些事件。app運行一個事件循環。它從它的事件隊列中獲取最老的事件,處理它,然後返回到下一個,處理它,以此類推,直到事件隊列爲空。

應用程序在整個運行過程中-您在屏幕上點擊,下載內容,計時器關閉-該事件循環不斷循環,一次處理一次這些事件。

事件循環一次處理一個事件。

當操作中斷時,線程就掛起,等待下一個事件。它可以觸發垃圾收集器,喝點咖啡什麼的。

所有Dart用於異步編程的高級api和語言特性—futures、流、異步和等待——都建立在這個簡單的循環之上。

例如,假設您有一個啓動網絡請求的按鈕,如下所示:

RaisedButton(
  child: Text('Click me'),
  onPressed: () {
    final myFuture = http.get('https://example.com');
    myFuture.then((response) {
      if (response.statusCode == 200) {
        print('Success!');
      }
    });
  },
)

當您運行應用程序時,Flutter會構建按鈕並將其顯示在屏幕上。 然後您的應用程序等待。

您應用的事件循環只是閒着,等待下一個事件。 與按鈕無關的事件可能會進入並得到處理,而按鈕坐在那裏等待用戶點擊它。 最終,他們這樣做了,點擊事件進入了隊列。

該事件被選中進行處理。Flutter看着它,渲染系統說,“那些座標匹配凸起的按鈕,”所以Flutter執行onPressed函數。該代碼啓動一個網絡請求(返回一個future),並使用then()方法爲future註冊一個完成處理程序。

就是這樣。 循環完成了對點擊事件的處理,並將其丟棄。

onPressedRaisedButton的一個屬性,網絡事件使用了future的回調,但這兩種技術做的是相同的基本事情。它們都是一種告訴Flutter的方式,“嘿,稍後,你可能會看到一個特定類型的事件。當你這樣做時,請執行這段代碼。”

因此,onPressed正在等待點擊,而future正在等待網絡數據,但是從Dart的角度來看,這些都只是隊列中的事件。

這就是在Dart中異步編碼的工作方式。Futures, streams, asyncawait——這些api都是你用來告訴Dart的事件循環的方法,“這裏有一些代碼,請稍後運行。”

如果我們回頭看代碼示例,您現在可以清楚地看到它是如何針對特定事件分解爲塊的。有初始構建(1)、點擊事件(2)和網絡響應事件(3)。

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!');
      }
    });
  },
)

習慣了使用異步代碼後,您將開始在各處識別這些模式。 在繼續使用更高級別的API時,瞭解事件循環將有所幫助。

總結

我們快速介紹了隔離、事件循環和Dart中異步編碼的基礎。如果您想了解更多細節,比如微任務隊列是如何工作的,請參閱過時的、已存檔但仍然深受喜愛的文章《事件循環和Dart》

要了解關於Dart中異步的更多信息,請參閱本系列的下一篇文章:Dart異步編程:Futures

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章