簡(jiǎn)介
之前介紹了很多dart中的異步編程技巧骄瓣,不知道大家有沒(méi)有發(fā)現(xiàn)一個(gè)問(wèn)題械馆,如果是在java的異步編程中嫉柴,肯定會(huì)提到鎖和并發(fā)機(jī)制厌杜,但是對(duì)于dart來(lái)說(shuō),好像從來(lái)沒(méi)有聽(tīng)到多線(xiàn)程和并發(fā)的問(wèn)題差凹,這是為什么呢期奔?
今天侧馅,給大家講解一下dart中的隔離機(jī)制危尿,大家就明白了。
dart中的隔離機(jī)制
dart是一個(gè)單線(xiàn)程的語(yǔ)言馁痴,但是作為一個(gè)單線(xiàn)程的語(yǔ)言谊娇,dart卻支持Future,Stream等異步特性。這一切都是隔離機(jī)制和事件循環(huán)帶來(lái)的結(jié)果罗晕。
首先看一下dart中的隔離機(jī)制济欢。
所謂隔離指的是dart運(yùn)行的一個(gè)特定的空間,這個(gè)空間擁有單獨(dú)的內(nèi)存和單線(xiàn)程的事件循環(huán)小渊。
如下圖所示:
在java或者c++等其他語(yǔ)言中法褥,多個(gè)線(xiàn)程是共享內(nèi)存空間的,雖然帶來(lái)了并發(fā)和數(shù)據(jù)溝通的方便途徑酬屉,但是同時(shí)也造成了并發(fā)編程的困難半等。
因?yàn)槲覀冃枰紤]多線(xiàn)程之間數(shù)據(jù)的同步揍愁,于是額外多出了很多鎖的機(jī)制,詳細(xì)了解或者用過(guò)的人應(yīng)該都會(huì)很煩惱杀饵。
多線(xiàn)程最大的缺陷就是要求程序員的羅輯思維和編程技巧足夠優(yōu)秀莽囤,這樣才能夠設(shè)計(jì)出完美運(yùn)行的多線(xiàn)程程序。
但是在dart中切距,這些都不是什么問(wèn)題朽缎。dart中所有的線(xiàn)程都擁有自己的運(yùn)行空間,這個(gè)線(xiàn)程的工作就是運(yùn)行事件循環(huán)谜悟。
那么問(wèn)題來(lái)了话肖,主線(xiàn)程在處理事件循環(huán),但是如果遇到了一個(gè)非常耗時(shí)的操作赌躺,該怎么辦呢? 如果直接在主線(xiàn)程中運(yùn)行狼牺,則可能會(huì)導(dǎo)致主線(xiàn)程的阻塞。
dart也充分考慮到了這個(gè)問(wèn)題礼患,所以dart提供了一個(gè)Isolate的類(lèi)來(lái)對(duì)隔離進(jìn)行管理是钥。
因?yàn)閐art程序本身就在一個(gè)Isolate中運(yùn)行,所以如果在dart中定義一個(gè)Isolate缅叠,那么這個(gè)Isolate通常表示的是另外一個(gè)悄泥,需要和當(dāng)前Isolate進(jìn)行通信的Isolate。
生成一個(gè)Isolate
那么如何在當(dāng)前的dart程序中生成一個(gè)Isolate呢肤粱?
Isolate提供了三種生成方法弹囚。
一個(gè)非常常用的是Isolate的工廠(chǎng)方法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會(huì)創(chuàng)建一個(gè)新的Isolate,調(diào)用它需要傳入幾個(gè)參數(shù):
entryPoint表示的是生成新Isolate的時(shí)候需要調(diào)用的函數(shù)领曼。entryPoint接受一個(gè)message參數(shù)鸥鹉。通常來(lái)說(shuō)message是一個(gè)SendPort對(duì)象,用于兩個(gè)Isolate之間的溝通庶骄。
paused表示新生成的Isolate是否處于暫停狀態(tài)毁渗,他相當(dāng)于:
isolate.pause(isolate.pauseCapability)
如果后續(xù)需要取消暫停狀態(tài),則可以調(diào)用:
isolate.resume(isolate.pauseCapability)
errorsAreFatal 對(duì)應(yīng)的是setErrorsFatal方法单刁。
onExit對(duì)應(yīng)的是addOnExitListener, onError對(duì)應(yīng)的是addErrorListener灸异。
debugName表示的是Isolate在調(diào)試的時(shí)候展示的名字。
如果spawn出錯(cuò)羔飞,則會(huì)拋出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方法生成的是和當(dāng)前代碼一樣的Isolate肺樟。如果想要使用不同的代碼來(lái)生成,則可以使用spawnUri,通過(guò)傳入對(duì)應(yīng)的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的構(gòu)造函數(shù):
Isolate(this.controlPort, {this.pauseCapability, this.terminateCapability});
它有三個(gè)參數(shù)卡儒,第一個(gè)參數(shù)是controlPort田柔,代表另外一個(gè)Isolate的控制權(quán)誓篱,后面兩個(gè)capabilities是原isolate的子集,表示是否有pause或者terminate的權(quán)限凯楔。
一般用法如下:
Isolate isolate = findSomeIsolate();
Isolate restrictedIsolate = Isolate(isolate.controlPort);
untrustedCode(restrictedIsolate);
Isolate之間的交互
所有的dart代碼都是運(yùn)行在Isolate中的窜骄,然后代碼只能夠訪(fǎng)問(wèn)同一個(gè)isolate內(nèi)的class和value。那么多個(gè)isolate之間通信摆屯,可以ReceivePort和SendPort來(lái)實(shí)現(xiàn)邻遏。
先看下SendPort,SendPort是Capability的一種:
abstract class SendPort implements Capability
SendPort用于向ReceivePort發(fā)送message, message可以有很多類(lèi)型,包括:
Null,bool,int,double,String,List,Map,TransferableTypedData,SendPort和Capability虐骑。
注意准验,send動(dòng)作是立馬完成的。
事實(shí)上廷没,SendPort是由ReceivePort來(lái)創(chuàng)建的糊饱。一個(gè)ReceivePort可以接收多個(gè)SendPort。
ReceivePort是Stream的一種:
abstract class ReceivePort implements Stream<dynamic>
作為Stream,它提供了一個(gè)listen用來(lái)處理接收到的消息:
StreamSubscription<dynamic> listen(void onData(var message)?,
{Function? onError, void onDone()?, bool? cancelOnError});
一個(gè)例子
講了那么多原理颠黎,有的同學(xué)可能會(huì)問(wèn)了另锋,那么到底怎么用呢?
例子來(lái)了:
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);
}
在主線(xiàn)程中狭归,我們創(chuàng)建了一個(gè)ReceivePort夭坪,然后調(diào)用了它的listen方法來(lái)監(jiān)聽(tīng)sendPort發(fā)過(guò)來(lái)的消息。
然后spawn出一個(gè)新的Isolate过椎,這個(gè)Isolate會(huì)在初始化之后室梅,調(diào)用entryPoint方法。
在這個(gè)entryPoint方法中又使用sendPort向ReceivePort發(fā)送消息疚宇。
最終運(yùn)行亡鼠,打印:
接收到消息 counter:0
總結(jié)
以上就是dart中的隔離機(jī)制和Isolate的使用敷待。
本文已收錄于 http://www.flydean.com/25-dart-isolates/
最通俗的解讀间涵,最深刻的干貨,最簡(jiǎn)潔的教程讼撒,眾多你不知道的小技巧等你來(lái)發(fā)現(xiàn)浑厚!
歡迎關(guān)注我的公眾號(hào):「程序那些事」,懂技術(shù)股耽,更懂你根盒!