Dart语言中的Isolate
Posted Androider_Zxg
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dart语言中的Isolate相关的知识,希望对你有一定的参考价值。
概述
Dart本身是Google推出的web开发语言,所以Dart语言在设计上很多方面都借鉴了JS。例如JS的面向对象、单线程、事件循环等。同时也做了许多优化,比如javascript低效的解释执行,而Dart可以在运行前直接编译为机器码,提高了执行效率,这个过程叫做AOT(Ahead of time)。还有JavaScript的单线程模型,只能依赖JS解释引擎的异步任务执行机制,开发者没有办法自己启动新的线程去执行耗时代码,但是Dart却提供了这个能力 — Isolate。Isolate是一种特殊的线程,多个Isolate之间内存独立不共享,而我们概念中的线程是共享其所在进程的内存的。个人理解是底层提供线程,应用层不能直接创建传统意义上的线程。只能使用底层提供的封装的Isolate,来实现在非主线程中进行耗时操作。
Isolate是什么
Isolate直译过来是“隔离”的意思,也就是各个Isolate是不干扰的,互相隔离的。
import 'dart:async';
import 'dart:isolate';
int i = 0;
IntObject intObject = IntObject();
void main() async
//Isolate的消息接收端口
final receive = ReceivePort();
//为消息接收端口添加监听
receive.listen((data)
print(
"Main: Receive data = $data, i = $i, intObject = $intObject.get()");
);
// 创建一个Isolate实例,1.指定入口函数 2.指定消息通信发送端口
Isolate isolate = await Isolate.spawn(isolateEntryFunction, receive.sendPort);
print(DateTime.now().toString() + " start...");
// isolate入口函数,该函数必须是静态的或顶级函数,不能是匿名内部函数。
void isolateEntryFunction(SendPort sendPort)
int counter = 0;
Timer.periodic(const Duration(seconds: 3), (_)
counter++;
//在单独的Isolate实例中修改i的值
i++;
intObject.increase();
String sendMsg = "Notification data: $counter";
print(DateTime.now().toString() +
" Isolate: counter = $counter, i = $i, intObject = $intObject.get()");
sendPort.send(sendMsg);
);
class IntObject
int _i = 0;
void increase()
_i++;
int get()
return _i;
结果:
2019-10-31 11:54:13.525943 start...
2019-10-31 11:54:16.537207 Isolate: counter = 1, i = 1, intObject = 1
Main: Receive data = Notification data: 1, i = 0, intObject = 0
2019-10-31 11:54:19.531389 Isolate: counter = 2, i = 2, intObject = 2
Main: Receive data = Notification data: 2, i = 0, intObject = 0
2019-10-31 11:54:22.527753 Isolate: counter = 3, i = 3, intObject = 3
Main: Receive data = Notification data: 3, i = 0, intObject = 0
2019-10-31 11:54:25.532155 Isolate: counter = 4, i = 4, intObject = 4
Main: Receive data = Notification data: 4, i = 0, intObject = 0
我们可以得到如下结论:
1、在新建的Isolate中修改i和intObject变量的值后,在main()函数所在Isolate中不能获取到,所以Isolate在内存上是隔离的。
2、Isolate虽然是隔离的,但提供了通信机制,叫做端口。
Isolate底层逻辑
虽然在内存表现上,Isolate内存隔离性像是进程的特点。但是从实现上不可能把Isolate作为一个进程,因为进程太重了,每新建一个进程,内核系统都会为新进程创建独立的虚拟内存,保存进程相关的数据结构,并且进程切换效率比较低。所以从可行性上来说Isolate的本质应该是一个线程。也就是说Isolate是通过线程实现的。我们使用多个Isolate也就是使用多个线程,只不过与传统线程不同的是,Isolate之间内存不共享,但可以通过通信机制互通。
从源码中验证,Isolate底层是线程。详情参考链接
Isolate如何实现内存隔离
通过跟踪Isolate的创建过程中,可以看到在新建一个Isolate实例后,会初始化一个Heap对象,并设置给Isolate。这个Heap应该就是当前Isolate独享的堆内存管理器。
// vm/isolate.cc
Isolate* Isolate::InitIsolate(const char* name_prefix,
IsolateGroup* isolate_group,
const Dart_IsolateFlags& api_flags,
bool is_vm_isolate)
Isolate* result = new Isolate(isolate_group, api_flags);
...
Heap::Init(result,
is_vm_isolate
? 0 // New gen size 0; VM isolate should only allocate in old.
: FLAG_new_gen_semi_max_size * MBInWords,
(is_service_or_kernel_isolate ? kDefaultMaxOldGenHeapSize
: FLAG_old_gen_heap_size) *
MBInWords);
...
从代码中可以看Isolate的堆内存也被区分为新生代和老年代两块区域,Dart虚拟机针对不同的区域执行不同的垃圾回收策略:
新生代采用复制清除算法,针对频繁创建销毁的页面控件对象,可以从内存层面实现快速分配和回收。
老年代采用标记清除和标记整理两种算法,来适应不同的内存回收场景,尽量保证UI的流畅性。
这里也就解释了在文章开头提到的Dart中简单内存分配模型,和高效垃圾的回收机制。
Isolate通信
创建Isolate时需要指定一个接收端口(ReceivePort)的发送端口(SendPort),调用者可以通过这个发送端口发送数据到其他的Isolate中ReceivePort的listen中,这种机制被称为消息传递(message passing)
既然是内存隔离的,那么在调用者所在Isolate发送的消息数据是怎么传递到接收者所在的Isolate中的呢?也就是说Isolate通信的底层逻辑是什么呢?
答案是map_变量,map_是一个Hash列表。是在Dart虚拟启动时初始化的,所以map_变量是存在于Dart虚拟机所属内存的,而这块内是各个Isolate共享的
为什么将Isolate设计成隔离的
1、首先说目前由移动端页面(包含android、ios、Web)构建的特性—树形结构构建布局、布局解析抽象、绘制、渲染,这一系列的复杂步骤导致必须在同一个线程完成。**因为多线程操作页面UI元素会有并发的问题,有并发就必须要加锁,加锁就会降低执行效率。所以强制在同一线程中操作UI是最好的选择。**况且在Flutter中,开发者面对的只有一个主Isolate,在Isolate中可以通过事件队列来实现异步(网络请求、文件IO)等。所以不需要再使用其他线程完成异步。
2、每当有页面交互时,必定会引起布局变化而重新绘制,这个过程会有频繁的大量的UI控件的创建和销毁,这就涉及到了耗时内存分配和回收。而这些较短生命周期的对象是存放在堆内存的新生代的,当虚拟机回收新生代内存时是要stop the world的,在Android或iOS中,各个线程共用一块堆内存,当非UI线程频繁申请、释放内存时也会触发垃圾回收,所以会间接影响UI线程的运行。
Dart为了解决这个问题,就每个Isolate(看做线程)分配各自的一块堆内存,并且独自管理内。这样的策略使得内存的分配和回收变得简单高效,并且不受其他Isolate的影响。
总结
1、Dart中向应用层提供了线程的封装——Isolate。应用层是不能创建线程的,只能使用Isolate
2、Isolate与传统的线程不同的是,内存隔离
3、Isolate设计成隔离的,是出去移动端页面UI构建特性考虑。第一点,UI绘制必须在同一线程内完成
所以强制同一线程是最好的选择。第二点,传统的线程内存共享,其他线程频繁的申请释放内存会触发垃圾回收,间接影响UI线程运行。
参考链接:https://blog.csdn.net/joye123/article/details/102913497
以上是关于Dart语言中的Isolate的主要内容,如果未能解决你的问题,请参考以下文章