一. isolate簡介
Dart 是單線程绸狐,Dart 為我們提供了 isolate,isolate 跟線程差不多驯耻,它可以理解為 Dart 中的線程养叛。isolate 與線程的區(qū)別就是線程與線程之間是共享內(nèi)存的,而 isolate 和 isolate 之間是內(nèi)存不共享的狠半,所以叫 isolate (隔離)噩死。因此也不存在鎖競(jìng)爭(zhēng)問題,兩個(gè)Isolate完全是兩條獨(dú)立的執(zhí)行線神年,且每個(gè)Isolate都有自己的事件循環(huán)已维,它們之間只能通過發(fā)送消息通信,所以它的資源開銷低于線程已日。
原文鏈接:https://blog.csdn.net/u011578734/article/details/108853613
大多數(shù)計(jì)算機(jī)中垛耳,甚至在移動(dòng)平臺(tái)上,都在使用多核 CPU。為了有效利用多核性能堂鲜,開發(fā)者一般使用共享內(nèi)存的方式讓線程并發(fā)地運(yùn)行栈雳。然而,多線程共享數(shù)據(jù)通常會(huì)導(dǎo)致很多潛在的問題缔莲,并導(dǎo)致代碼運(yùn)行出錯(cuò)哥纫。
為了解決多線程帶來的并發(fā)問題,Dart 使用 isolates 替代線程痴奏,所有的 Dart 代碼均運(yùn)行在一個(gè) isolates 中蛀骇。每一個(gè) isolates 有它自己的堆內(nèi)存以確保其狀態(tài)不被其它 isolates 訪問。
每個(gè) isolate 都擁有自己的事件循環(huán)及隊(duì)列(MicroTask 和 Event)读拆。這意味著在一個(gè) isolate 中運(yùn)行的代碼與另外一個(gè) isolate 不存在任何關(guān)聯(lián)擅憔。
isolate是Dart對(duì)actor并發(fā)模式的實(shí)現(xiàn)。運(yùn)行中的Dart程序由一個(gè)或多個(gè)actor組成檐晕,這些actor也就是Dart概念里面的isolate暑诸。isolate是有自己的內(nèi)存和單線程控制的運(yùn)行實(shí)體。isolate本身的意思是“隔離”棉姐,因?yàn)閕solate之間的內(nèi)存在邏輯上是隔離的屠列。isolate中的代碼是按順序執(zhí)行的,任何Dart程序的并發(fā)都是運(yùn)行多個(gè)isolate的結(jié)果伞矩。因?yàn)镈art沒有共享內(nèi)存的并發(fā)笛洛,沒有競(jìng)爭(zhēng)的可能性所以不需要鎖,也就不用擔(dān)心死鎖的問題乃坤。
二. isolate與async關(guān)系:
之前介紹過 async/await Future的原理, async關(guān)鍵字實(shí)現(xiàn)了異步操作, 而其所謂的異步其實(shí)也是運(yùn)行在同一線程中并沒有開啟新的線程, 只是通過單線程的任務(wù)調(diào)度實(shí)現(xiàn)一個(gè)先執(zhí)行其他的代碼片段苛让,等這邊有結(jié)果后再返回的異步效果.
- Isolate可以實(shí)現(xiàn)異步并行多個(gè)任務(wù)
- Future實(shí)現(xiàn)異步串行多個(gè)任務(wù)
舉個(gè)栗子:
假設(shè)有一個(gè)任務(wù),需要計(jì)算1+2+...100的和
我們通常會(huì)這么寫:
class IsolateTestPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return IsolateTestPageState();
}
}
class IsolateTestPageState extends State<IsolateTestPage> {
var content = "點(diǎn)擊計(jì)算按鈕,開始計(jì)算";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Isolate"),
),
body: SafeArea(
child: Center(
child: Column(
children: <Widget>[
Container(
width: double.infinity, height: 500, child: Text(content)),
TextButton(
child: Text('計(jì)算'),
onPressed: () {
int result = sum(100);
content = "總和$result";
setState(() {});
},
)
],
),
),
),
);
}
//計(jì)算0到 num 數(shù)值的總和
int sum(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
}
試一下,結(jié)果是可以的;但是如果我們改為計(jì)算1+2+...100000000000的和,運(yùn)行代碼會(huì)發(fā)現(xiàn),直接卡死了
這時(shí)候就需要?jiǎng)?chuàng)建一個(gè)isolate來執(zhí)行這個(gè)耗時(shí)的任務(wù),而不影響其他任務(wù)的執(zhí)行. 那么首先,如何創(chuàng)建一個(gè)isolate?
三.創(chuàng)建isolate 及 isolate通信
- (1).獲取當(dāng)前main isolate的ReceivePort及SendPort
- (2).使用Isolate.spawn創(chuàng)建新的isolate,需要傳入新的isolate需要完成的任務(wù)名稱及創(chuàng)建者(main isolate)的sendPort. (用于將新的isolate的sendPort傳遞給創(chuàng)建者)
- (3).在任務(wù)方法中,獲取新的isolate的ReceivePort及SendPort
- (4).將新的isolate的SendPort,通過main isolate的sendPort,發(fā)送給main isolate,使新的isolate的SendPort能在main isolate中發(fā)送消息
- (5). SendPort發(fā)送消息, ReceivePort接收消息,互相通信
1. receivePort.listen((message) {}) 能收到其對(duì)應(yīng)的sendPort發(fā)送的消息
_testIsolate() async {
ReceivePort rp1 = new ReceivePort();
SendPort port1 = rp1.sendPort;
// 通過spawn新建一個(gè)isolate,并綁定靜態(tài)方法
Isolate newIsolate = await Isolate.spawn(doWork, port1);
SendPort port2;
rp1.listen((message) {
print("rp1 收到消息: $message"); //2. 4. 7.rp1收到消息
if (message[0] == 0) {
port2 = message[1]; //得到rp2的發(fā)送器port2
} else {
if (port2 != null) {
print("port2 發(fā)送消息");
port2?.send([1, "這條信息是 port2 在main isolate中 發(fā)送的"]); // 8.port2發(fā)送消息
}
}
});
print("port1--main isolate發(fā)送消息");
port1.send([1, "這條信息是 port1 在main isolate中 發(fā)送的"]); //1.port1發(fā)送消息
// newIsolate.kill();
}
// 新的isolate中可以處理耗時(shí)任務(wù)
static void doWork(SendPort port1) {
ReceivePort rp2 = new ReceivePort();
SendPort port2 = rp2.sendPort;
rp2.listen((message) {
//9.10 rp2收到消息
print("rp2 收到消息: $message");
});
// 將新isolate中創(chuàng)建的SendPort發(fā)送到main isolate中用于通信
print("port1--new isolate發(fā)送消息");
port1.send([0, port2]); //3.port1發(fā)送消息,傳遞[0,rp2的發(fā)送器]
// 模擬耗時(shí)5秒
sleep(Duration(seconds: 5));
print("port1--new isolate發(fā)送消息");
port1.send([1, "這條信息是 port1 在new isolate中 發(fā)送的"]); //5.port1發(fā)送消息
print("port2--new isolate發(fā)送消息");
port2.send([1, "這條信息是 port2 在new isolate中 發(fā)送的"]); //6.port2發(fā)送消息
}
//I/flutter (14639): port1--main isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [1, 這條信息是 port1 在main isolate中 發(fā)送的]
//I/flutter (14639): port1--new isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [0, SendPort]
//I/flutter (14639): port1--new isolate發(fā)送消息
//I/flutter (14639): port2--new isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [1, 這條信息是 port1 在new isolate中 發(fā)送的]
//I/flutter (14639): port2 發(fā)送消息
//I/flutter (14639): rp2 收到消息: [1, 這條信息是 port2 在new isolate中 發(fā)送的]
//I/flutter (14639): rp2 收到消息: [1, 這條信息是 port2 在main isolate中 發(fā)送的]
2. dynamic result = await receivePort.first; 只能收到第一條消息
_testIsolate() async {
ReceivePort rp1 = new ReceivePort();
SendPort port1 = rp1.sendPort;
// 通過spawn新建一個(gè)isolate湿诊,并綁定靜態(tài)方法
Isolate newIsolate = await Isolate.spawn(doWork, port1);
SendPort port2;
dynamic receiveMsg = await rp1.first; //只拿到第一條收到結(jié)果
print('rp1 收到消息--$receiveMsg');
if (receiveMsg is SendPort) {
SendPort port2 = receiveMsg;
// print('rp1 收到消息--port2');
port2.send([1, "這條信息是 port2 在main isolate中 發(fā)送的"]);
}
// newIsolate.kill();
}
// 新的isolate中可以處理耗時(shí)任務(wù)
static void doWork(SendPort port1) {
ReceivePort rp2 = new ReceivePort();
SendPort port2 = rp2.sendPort;
rp2.listen((message) {
print("rp2 收到消息-- $message");
});
// 將新isolate中創(chuàng)建的SendPort發(fā)送到main isolate中用于通信
print("port1--new isolate發(fā)送消息--port2");
port1.send(port2);
// 模擬耗時(shí)5秒
sleep(Duration(seconds: 5));
print("port1--new isolate發(fā)送消息--啊哈哈");
port1.send("啊哈哈");
}
3.解決示例問題
知道怎么創(chuàng)建isolate了,我們?cè)倏丛趺磥碛?jì)算1+2+...100000000000的和
(1)使用listen監(jiān)聽,結(jié)果回調(diào)的形式
1.創(chuàng)建isolate
2.打通兩個(gè)isolate的通道(能互相發(fā)送消息)
3.main isolate將要計(jì)算的最大數(shù)傳遞給new isolate; newisolate計(jì)算,計(jì)算完成后,將結(jié)果發(fā)送回 main isolate
calculation(int n, Function(int result) success) async {
//創(chuàng)建一個(gè)ReceivePort
final receivePort1 = new ReceivePort();
//創(chuàng)建isolate
Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);
receivePort1.listen((message) {
if (message is SendPort) {
SendPort sendPort2 = message;
sendPort2.send(n);
} else {
print(message);
success(message);
}
});
}
//創(chuàng)建isolate必須要的參數(shù)
static void createIsolate(SendPort sendPort1) {
final receivePort2 = new ReceivePort();
//綁定
print("sendPort1發(fā)送消息--sendPort2");
sendPort1.send(receivePort2.sendPort);
//監(jiān)聽
receivePort2.listen((message) {
//獲取數(shù)據(jù)并解析
print("receivePort2接收到消息--$message");
if (message is int) {
num result = summ(message);
sendPort1.send(result);
}
});
}
//計(jì)算0到 num 數(shù)值的總和
static num summ(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
(2)receivePort.first只能收到第一條消息; 我們可以再創(chuàng)建一個(gè)ReceivePort用來傳遞消息,如下:
static Future<dynamic> calculation(int n) async {
//創(chuàng)建一個(gè)ReceivePort
final receivePort1 = new ReceivePort();
//創(chuàng)建isolate
Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);
//使用 receivePort1.first 獲取sendPort1發(fā)送來的數(shù)據(jù)
final sendPort2 = await receivePort1.first as SendPort;
print("receivePort1接收到消息--sendPort2");
//接收消息的ReceivePort
final answerReceivePort = new ReceivePort();
print("sendPort2發(fā)送消息--[$n,answerSendPort]");
sendPort2.send([n, answerReceivePort.sendPort]);
//獲得數(shù)據(jù)并返回
num result = await answerReceivePort.first;
print("answerReceivePort接收到消息--計(jì)算結(jié)果$result");
return result;
}
//創(chuàng)建isolate必須要的參數(shù)
static void createIsolate(SendPort sendPort1) {
final receivePort2 = new ReceivePort();
//綁定
print("sendPort1發(fā)送消息--sendPort2");
sendPort1.send(receivePort2.sendPort);
//監(jiān)聽
receivePort2.listen((message) {
//獲取數(shù)據(jù)并解析
print("receivePort2接收到消息--$message");
final n = message[0] as num;
final send = message[1] as SendPort;
//返回結(jié)果
num result = summ(n);
print("answerSendPort發(fā)送消息--計(jì)算結(jié)果$result");
send.send(result);
});
}
//計(jì)算0到 num 數(shù)值的總和
static num summ(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
4. isolate的暫停 恢復(fù) 結(jié)束
//恢復(fù) isolate 的使用
isolate.resume(isolate.pauseCapability);
//暫停 isolate 的使用
isolate.pause(isolate.pauseCapability);
//結(jié)束 isolate 的使用
isolate.kill(priority: Isolate.immediate);
//賦值為空 便于內(nèi)存及時(shí)回收
isolate = null;
四.flutter中創(chuàng)建isolate---compute()方法
TextButton(
child: Text('flutter創(chuàng)建isolate'),
onPressed: () async {
num result = await compute(summ, 10000000000);
content = "計(jì)算結(jié)果$result";
setState(() {});
},
),