dart系列之:dart优秀的秘诀-隔离机制
文章目錄
- 簡介
- dart中的隔離機制
- 生成一個Isolate
- Isolate之間的交互
- 一個例子
- 總結
簡介
之前介紹了很多dart中的異步編程技巧,不知道大家有沒有發現一個問題,如果是在java的異步編程中,肯定會提到鎖和并發機制,但是對于dart來說,好像從來沒有聽到多線程和并發的問題,這是為什么呢?
今天,給大家講解一下dart中的隔離機制,大家就明白了。
dart中的隔離機制
dart是一個單線程的語言,但是作為一個單線程的語言,dart卻支持Future,Stream等異步特性。這一切都是隔離機制和事件循環帶來的結果。
首先看一下dart中的隔離機制。
所謂隔離指的是dart運行的一個特定的空間,這個空間擁有單獨的內存和單線程的事件循環。
如下圖所示:
在java或者c++等其他語言中,多個線程是共享內存空間的,雖然帶來了并發和數據溝通的方便途徑,但是同時也造成了并發編程的困難。
因為我們需要考慮多線程之間數據的同步,于是額外多出了很多鎖的機制,詳細了解或者用過的人應該都會很煩惱。
多線程最大的缺陷就是要求程序員的羅輯思維和編程技巧足夠優秀,這樣才能夠設計出完美運行的多線程程序。
但是在dart中,這些都不是什么問題。dart中所有的線程都擁有自己的運行空間,這個線程的工作就是運行事件循環。
那么問題來了,主線程在處理事件循環,但是如果遇到了一個非常耗時的操作,該怎么辦呢? 如果直接在主線程中運行,則可能會導致主線程的阻塞。
dart也充分考慮到了這個問題,所以dart提供了一個Isolate的類來對隔離進行管理。
因為dart程序本身就在一個Isolate中運行,所以如果在dart中定義一個Isolate,那么這個Isolate通常表示的是另外一個,需要和當前Isolate進行通信的Isolate。
生成一個Isolate
那么如何在當前的dart程序中生成一個Isolate呢?
Isolate提供了三種生成方法。
一個非常常用的是Isolate的工廠方法spawn:
external static Future<Isolate> spawn<T>(void entryPoint(T message), T message,{bool paused = false,bool errorsAreFatal = true,SendPort? onExit,SendPort? onError,@Since("2.3") String? debugName});spawn會創建一個新的Isolate,調用它需要傳入幾個參數:
entryPoint表示的是生成新Isolate的時候需要調用的函數。entryPoint接受一個message參數。通常來說message是一個SendPort對象,用于兩個Isolate之間的溝通。
paused表示新生成的Isolate是否處于暫停狀態,他相當于:
isolate.pause(isolate.pauseCapability)如果后續需要取消暫停狀態,則可以調用:
isolate.resume(isolate.pauseCapability)errorsAreFatal 對應的是setErrorsFatal方法。
onExit對應的是addOnExitListener, onError對應的是addErrorListener。
debugName表示的是Isolate在調試的時候展示的名字。
如果spawn出錯,則會拋出IsolateSpawnException異常:
class IsolateSpawnException implements Exception {/// Error message reported by the spawn operation.final String message;@pragma("vm:entry-point")IsolateSpawnException(this.message);String toString() => "IsolateSpawnException: $message"; }spawn方法生成的是和當前代碼一樣的Isolate。如果想要使用不同的代碼來生成,則可以使用spawnUri,通過傳入對應的Uri地址,從而生成不一樣的code。
external static Future<Isolate> spawnUri(Uri uri,List<String> args,var message,{bool paused = false,SendPort? onExit,SendPort? onError,bool errorsAreFatal = true,bool? checked,Map<String, String>? environment,@Deprecated('The packages/ dir is not supported in Dart 2')Uri? packageRoot,Uri? packageConfig,bool automaticPackageResolution = false,@Since("2.3")String? debugName});還有一種方式,就是使用Isolate的構造函數:
Isolate(this.controlPort, {this.pauseCapability, this.terminateCapability});它有三個參數,第一個參數是controlPort,代表另外一個Isolate的控制權,后面兩個capabilities是原isolate的子集,表示是否有pause或者terminate的權限。
一般用法如下:
Isolate isolate = findSomeIsolate(); Isolate restrictedIsolate = Isolate(isolate.controlPort); untrustedCode(restrictedIsolate);Isolate之間的交互
所有的dart代碼都是運行在Isolate中的,然后代碼只能夠訪問同一個isolate內的class和value。那么多個isolate之間通信,可以ReceivePort和SendPort來實現。
先看下SendPort,SendPort是Capability的一種:
abstract class SendPort implements CapabilitySendPort用于向ReceivePort發送message, message可以有很多類型,包括:
Null,bool,int,double,String,List,Map,TransferableTypedData,SendPort和Capability。
注意,send動作是立馬完成的。
事實上,SendPort是由ReceivePort來創建的。一個ReceivePort可以接收多個SendPort。
ReceivePort是Stream的一種:
abstract class ReceivePort implements Stream<dynamic>作為Stream,它提供了一個listen用來處理接收到的消息:
StreamSubscription<dynamic> listen(void onData(var message)?,{Function? onError, void onDone()?, bool? cancelOnError});一個例子
講了那么多原理,有的同學可能會問了,那么到底怎么用呢?
例子來了:
import 'dart:isolate';var isolate;void entryPoint(SendPort sendPort) {int counter = 0;sendPort.send("counter:$counter"); } void main() async{final receiver = ReceivePort();receiver.listen((message) {print( "接收到消息 $message");});isolate = await Isolate.spawn(entryPoint, receiver.sendPort); }在主線程中,我們創建了一個ReceivePort,然后調用了它的listen方法來監聽sendPort發過來的消息。
然后spawn出一個新的Isolate,這個Isolate會在初始化之后,調用entryPoint方法。
在這個entryPoint方法中又使用sendPort向ReceivePort發送消息。
最終運行,打印:
接收到消息 counter:0總結
以上就是dart中的隔離機制和Isolate的使用。
本文已收錄于 http://www.flydean.com/25-dart-isolates/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!
總結
以上是生活随笔為你收集整理的dart系列之:dart优秀的秘诀-隔离机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dart系列之:安全看我,dart中的安
- 下一篇: netty系列之:从零到壹,搭建一个SO