flutter-isolate详解
一. isolate简介
Dart 是单线程,Dart 为我们提供了 isolate,isolate 跟线程差不多,它可以理解为 Dart 中的线程。isolate 与线程的区别就是线程与线程之间是共享内存的,而 isolate 和 isolate 之间是内存不共享的,所以叫 isolate (隔离)。因此也不存在锁竞争问题,两个Isolate完全是两条独立的执行线,且每个Isolate都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。
原文链接:https://blog.csdn.net/u011578734/article/details/108853613
大多数计算机中,甚至在移动平台上,都在使用多核 CPU。为了有效利用多核性能,开发者一般使用共享内存的方式让线程并发地运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。
为了解决多线程带来的并发问题,Dart 使用 isolates 替代线程,所有的 Dart 代码均运行在一个 isolates 中。每一个 isolates 有它自己的堆内存以确保其状态不被其它 isolates 访问。
每个 isolate 都拥有自己的事件循环及队列(MicroTask 和 Event)。这意味着在一个 isolate 中运行的代码与另外一个 isolate 不存在任何关联。
isolate是Dart对actor并发模式的实现。运行中的Dart程序由一个或多个actor组成,这些actor也就是Dart概念里面的isolate。isolate是有自己的内存和单线程控制的运行实体。isolate本身的意思是“隔离”,因为isolate之间的内存在逻辑上是隔离的。isolate中的代码是按顺序执行的,任何Dart程序的并发都是运行多个isolate的结果。因为Dart没有共享内存的并发,没有竞争的可能性所以不需要锁,也就不用担心死锁的问题。
二. isolate与async关系:
之前介绍过 async/await Future的原理, async关键字实现了异步操作, 而其所谓的异步其实也是运行在同一线程中并没有开启新的线程, 只是通过单线程的任务调度实现一个先执行其他的代码片段,等这边有结果后再返回的异步效果.
- Isolate可以实现异步并行多个任务
- Future实现异步串行多个任务
举个栗子:
假设有一个任务,需要计算1+2+...100的和
我们通常会这么写:
class IsolateTestPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return IsolateTestPageState();
}
}
class IsolateTestPageState extends State<IsolateTestPage> {
var content = "点击计算按钮,开始计算";
@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('计算'),
onPressed: () {
int result = sum(100);
content = "总和$result";
setState(() {});
},
)
],
),
),
),
);
}
//计算0到 num 数值的总和
int sum(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
}
试一下,结果是可以的;但是如果我们改为计算1+2+...100000000000的和,运行代码会发现,直接卡死了
这时候就需要创建一个isolate来执行这个耗时的任务,而不影响其他任务的执行. 那么首先,如何创建一个isolate?
三.创建isolate 及 isolate通信
- (1).获取当前main isolate的ReceivePort及SendPort
- (2).使用Isolate.spawn创建新的isolate,需要传入新的isolate需要完成的任务名称及创建者(main isolate)的sendPort. (用于将新的isolate的sendPort传递给创建者)
- (3).在任务方法中,获取新的isolate的ReceivePort及SendPort
- (4).将新的isolate的SendPort,通过main isolate的sendPort,发送给main isolate,使新的isolate的SendPort能在main isolate中发送消息
- (5). SendPort发送消息, ReceivePort接收消息,互相通信
1. receivePort.listen((message) {}) 能收到其对应的sendPort发送的消息
_testIsolate() async {
ReceivePort rp1 = new ReceivePort();
SendPort port1 = rp1.sendPort;
// 通过spawn新建一个isolate,并绑定静态方法
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的发送器port2
} else {
if (port2 != null) {
print("port2 发送消息");
port2?.send([1, "这条信息是 port2 在main isolate中 发送的"]); // 8.port2发送消息
}
}
});
print("port1--main isolate发送消息");
port1.send([1, "这条信息是 port1 在main isolate中 发送的"]); //1.port1发送消息
// newIsolate.kill();
}
// 新的isolate中可以处理耗时任务
static void doWork(SendPort port1) {
ReceivePort rp2 = new ReceivePort();
SendPort port2 = rp2.sendPort;
rp2.listen((message) {
//9.10 rp2收到消息
print("rp2 收到消息: $message");
});
// 将新isolate中创建的SendPort发送到main isolate中用于通信
print("port1--new isolate发送消息");
port1.send([0, port2]); //3.port1发送消息,传递[0,rp2的发送器]
// 模拟耗时5秒
sleep(Duration(seconds: 5));
print("port1--new isolate发送消息");
port1.send([1, "这条信息是 port1 在new isolate中 发送的"]); //5.port1发送消息
print("port2--new isolate发送消息");
port2.send([1, "这条信息是 port2 在new isolate中 发送的"]); //6.port2发送消息
}
//I/flutter (14639): port1--main isolate发送消息
//I/flutter (14639): rp1 收到消息: [1, 这条信息是 port1 在main isolate中 发送的]
//I/flutter (14639): port1--new isolate发送消息
//I/flutter (14639): rp1 收到消息: [0, SendPort]
//I/flutter (14639): port1--new isolate发送消息
//I/flutter (14639): port2--new isolate发送消息
//I/flutter (14639): rp1 收到消息: [1, 这条信息是 port1 在new isolate中 发送的]
//I/flutter (14639): port2 发送消息
//I/flutter (14639): rp2 收到消息: [1, 这条信息是 port2 在new isolate中 发送的]
//I/flutter (14639): rp2 收到消息: [1, 这条信息是 port2 在main isolate中 发送的]
2. dynamic result = await receivePort.first; 只能收到第一条消息
_testIsolate() async {
ReceivePort rp1 = new ReceivePort();
SendPort port1 = rp1.sendPort;
// 通过spawn新建一个isolate,并绑定静态方法
Isolate newIsolate = await Isolate.spawn(doWork, port1);
SendPort port2;
dynamic receiveMsg = await rp1.first; //只拿到第一条收到结果
print('rp1 收到消息--$receiveMsg');
if (receiveMsg is SendPort) {
SendPort port2 = receiveMsg;
// print('rp1 收到消息--port2');
port2.send([1, "这条信息是 port2 在main isolate中 发送的"]);
}
// newIsolate.kill();
}
// 新的isolate中可以处理耗时任务
static void doWork(SendPort port1) {
ReceivePort rp2 = new ReceivePort();
SendPort port2 = rp2.sendPort;
rp2.listen((message) {
print("rp2 收到消息-- $message");
});
// 将新isolate中创建的SendPort发送到main isolate中用于通信
print("port1--new isolate发送消息--port2");
port1.send(port2);
// 模拟耗时5秒
sleep(Duration(seconds: 5));
print("port1--new isolate发送消息--啊哈哈");
port1.send("啊哈哈");
}
3.解决示例问题
知道怎么创建isolate了,我们再看怎么来计算1+2+...100000000000的和
(1)使用listen监听,结果回调的形式
1.创建isolate
2.打通两个isolate的通道(能互相发送消息)
3.main isolate将要计算的最大数传递给new isolate; newisolate计算,计算完成后,将结果发送回 main isolate
calculation(int n, Function(int result) success) async {
//创建一个ReceivePort
final receivePort1 = new ReceivePort();
//创建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);
}
});
}
//创建isolate必须要的参数
static void createIsolate(SendPort sendPort1) {
final receivePort2 = new ReceivePort();
//绑定
print("sendPort1发送消息--sendPort2");
sendPort1.send(receivePort2.sendPort);
//监听
receivePort2.listen((message) {
//获取数据并解析
print("receivePort2接收到消息--$message");
if (message is int) {
num result = summ(message);
sendPort1.send(result);
}
});
}
//计算0到 num 数值的总和
static num summ(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
(2)receivePort.first只能收到第一条消息; 我们可以再创建一个ReceivePort用来传递消息,如下:
static Future<dynamic> calculation(int n) async {
//创建一个ReceivePort
final receivePort1 = new ReceivePort();
//创建isolate
Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);
//使用 receivePort1.first 获取sendPort1发送来的数据
final sendPort2 = await receivePort1.first as SendPort;
print("receivePort1接收到消息--sendPort2");
//接收消息的ReceivePort
final answerReceivePort = new ReceivePort();
print("sendPort2发送消息--[$n,answerSendPort]");
sendPort2.send([n, answerReceivePort.sendPort]);
//获得数据并返回
num result = await answerReceivePort.first;
print("answerReceivePort接收到消息--计算结果$result");
return result;
}
//创建isolate必须要的参数
static void createIsolate(SendPort sendPort1) {
final receivePort2 = new ReceivePort();
//绑定
print("sendPort1发送消息--sendPort2");
sendPort1.send(receivePort2.sendPort);
//监听
receivePort2.listen((message) {
//获取数据并解析
print("receivePort2接收到消息--$message");
final n = message[0] as num;
final send = message[1] as SendPort;
//返回结果
num result = summ(n);
print("answerSendPort发送消息--计算结果$result");
send.send(result);
});
}
//计算0到 num 数值的总和
static num summ(int num) {
int count = 0;
while (num > 0) {
count = count + num;
num--;
}
return count;
}
4. isolate的暂停 恢复 结束
//恢复 isolate 的使用
isolate.resume(isolate.pauseCapability);
//暂停 isolate 的使用
isolate.pause(isolate.pauseCapability);
//结束 isolate 的使用
isolate.kill(priority: Isolate.immediate);
//赋值为空 便于内存及时回收
isolate = null;
四.flutter中创建isolate---compute()方法
TextButton(
child: Text('flutter创建isolate'),
onPressed: () async {
num result = await compute(summ, 10000000000);
content = "计算结果$result";
setState(() {});
},
),