[Flutter翻譯]Flutter/Dart非阻塞解惑

Sunbreak發表於2021-04-07

原文地址:medium.com/flutter-com…

原文作者:medium.com/@truongsinh

釋出時間:2019年6月11日 - 5分鐘閱讀

更新:巧合的是,Flutter團隊剛剛釋出了一個片段。它直觀漂亮地說明了非阻塞概念。在下面的文章中,聲稱Dart不是單執行緒,而這個視訊卻聲稱不是。在研究和討論更多的時候,我就不寫文章了。

www.youtube.com/watch?v=vl_…

TLDR:Flutter/Dart不是單執行緒;Dart的併發模型不是Java的執行緒;Future/Async/Await執行在同一個執行緒上,解決IO繫結的問題,而Dart的Isolate/Flutter的計算執行在不同的隔離(無共享記憶體)執行緒上,解決CPU繫結的問題。

image.png

誤區一:Flutter/Dart是單執行緒的。

在網上,我看到有人在說Dart是單執行緒語言,因此Flutter也是單執行緒語言。我想說,說Dart是單執行緒語言在技術上是不對的。讓我來分析一下。

任何一段Dart程式碼都是在單執行緒中執行的,這是事實。也就是說,任何一段程式碼,沒有回撥和等待關鍵字,都可以保證不間斷的執行。

但是,在我看來,單執行緒語言是一種老舊的程式語言,無法利用多核處理器的優勢。為了簡單起見,我們說單執行緒語言是併發語言的反面(雖然併發不是並行)。換句話說,在多核處理器上,用併發語言正確編寫的應用程式可以並行進行多項計算,利用多核,而用單執行緒語言編寫的應用程式的效能並不比執行在單核上的應用程式好。

其實Dart使用Isolates支援多執行緒(有點,見下一個誤區),有了這個,Flutter就可以在移動或桌面上充分利用現代多核處理器的優勢。

誤區二:Flutter/Dart的併發模型和Java的執行緒一樣。

正如迷思1中所解釋的,Dart使用Isolates支援多執行緒。就在Isolates的介紹中,有人說過

isolates[是]獨立的工作者,它們類似於執行緒,但不共享記憶體,只通過訊息進行通訊。

基本上,它類似於最新的瀏覽器/JavaScript的Web Workers API或NodeJS的Worker Threads。補充一點,按照神話1中的定義,JavaScript在沒有Web Workers或Worker Threads API之前,一直是單執行緒語言(Node的Cluster API是關於fork/process的,不是執行緒)。

有些人在遇到問題時,會想:"我知道了,我就用執行緒吧。"然後二他們就會出現erpoblesms。

因為Dart使用的是訊息傳遞模式,而且沒有共享記憶體/變數,所以幾乎不需要鎖或mutex。另外,我們還注意到

如果你試圖在隔離體之間傳遞更復雜的物件,如Future或http.Response,你可能會遇到錯誤。

簡單來說,大多數時候,Dart的隔離體之間的通訊必須是可序列化的。

誤區3:Future/Async/Await在單獨的執行緒中執行程式碼,解決了所有阻塞問題。

完全不對。Future/Async/Await在同一個執行緒中執行程式碼。如果需要等待外部資料/事件,則應使用Dart的事件佇列,但仍在同一執行緒中。因此,Future/Async/Await只解決了一半的阻塞問題。

為了更好的理解,我們應該退一步講。程式設計中的阻塞可以分為2類,I/O繫結和CPU繫結。

I/O指的是運動中的資料,比如網路HTTP請求/響應、向本地儲存讀/寫資料、執行緒/隔離區之間的訊息傳遞(見迷思2)。比如我們發出一個HTTP請求,等待HTTP響應的時候,等待的時間可能是幾秒鐘,在這幾秒鐘裡,如果我們的應用寫得不好,可能會被阻塞,UI也會被凍結。

在Java/Kotlin或ObjC/Swift中,預設情況下,大部分IO繫結的操作都是阻塞的,開發者必須有意識地做一些額外的程式碼,通過各種技術讓它 "脫離""主執行緒"。在Dart中,預設情況下,所有IO繫結的操作都是async並返回Future。開發者可以通過附加Sync字首來選擇使用阻塞變化,比如File的方法copy與copySync,lastAccessed與lastAccessedSync。

但是,Future/Async/Await並不能解除對CPU繫結操作的封鎖。看看這個簡單的例子

在這種情況下,使用Future/Async/Await其實會讓效能差很多,比如差180倍(哦,調整param的時候要注意,veryLongRunningCpuBoundFunction的設計是有O(nⁿ)時間複雜度?的),但並不能解決阻塞問題。如果你不相信,只需複製貼上veryLongRunningCpuBoundFunction(阻塞版或非同步版),然後在你的Flutter應用中執行,只要呼叫該函式,你的UI就會被完全凍結。

veryLongRunningCpuBoundFunction可能看起來不切實際的簡單和/或愚蠢。實際上,常見的CPU繫結操作有。

在這種情況下,我們應該使用我們在前面2個迷思中一直在討論的工具,Isolate。Dart的Isolate API,在我看來,並不是最容易使用的。幸運的是,Flutter有一個實用函式compute,它基本上是Dart的Isolate的一個包裝器,API要簡單得多(但有些有限)。事實上,Flutter的例子是關於在後臺解析JSON的(解析JSON被歸入CPU繫結操作的序列化/反序列化情況)。另外值得注意的是,compute返回了一個Future,因為CPU繫結的程式碼(解析JSON)現在執行在不同的隔離區,而隔離區的通訊(訊息傳遞)是IO繫結的。

重新審視上面的例子,這裡是執行程式碼而不使UI凍結的正確方法。

同樣值得注意的是,compute返回的是Future,但veryLongRunningCpuBoundFunction本身返回的是一個基後設資料型別(本例中為int)。

Dart的Isolate / Flutter的compute對於從iOS/Android背景來Flutter的人來說是極其重要的一點。

Dart's Isolate / Flutter的計算對於從iOS/Android背景來Flutter的人來說是一個極其重要的點。如上所述,iOS/Android傳統上並不區分IO繫結和CPU繫結的阻塞操作,因此開發者總是有意識地手動使用相同的技術來處理任何阻塞操作(Android上的執行緒+Future/Callback/Stream,iOS上的GCD)。在Dart/Flutter中,所有IO繫結的阻塞操作都已經被語言用Future識別並解決了,但新手要注意Dart的Isolate/Flutter的計算,以避免UI被凍結。

結語

Flutter/Dart在技術上不是單執行緒的,儘管Dart程式碼是在單執行緒中執行的。Dart是一種預設的執行緒安全的、可並行的、完全無阻塞的、帶有訊息傳遞模式的語言,可以充分利用現代多核架構的優勢,而不用擔心鎖和mutex的問題。Dart中的阻塞可以是I/O繫結的,也可以是CPU繫結的,分別應該由Future和Dart的Isolate/Flutter的計算來解決。


www.deepl.com 翻譯

相關文章