Flutter入门到进阶Dart进阶篇---多线程异步Isolate
1 Isolate
1.1 什么是Isolate
1.1.1 概念
线程?异步?隔离?到底什么意思?
Isolate中文意思是隔离,从使用角度来说是Dart的线程,但是从本质虚拟机的实现角度来讲Isolate是一组封装。
isolate可以理解为dart中的线程,但它又不同于线程,准确的说应该叫做协程,协程最大的优势就是它具有极高的执行效率,因为协程中子程序的调用不需要线程的切换,所以对于线程数量越大的程序来说协程的优势就越明显。每个isolate都有自己独立的执行线程和事件循环,以及内存,所以isolate之间不存在锁竞争的问题,各个isolate之间通过消息通信。
1.1.2 图例
1.1.3 设计目的
首先说目前由移动端页面(包含Android、iOS、Web)构建的特性—树形结构构建布局、布局解析抽象、绘制、渲染,这一系列的复杂步骤导致必须在同一个线程完成。因为多线程操作页面UI元素会有并发的问题,有并发就必须要加锁,加锁就会降低执行效率。所以强制在同一线程中操作UI是最好的选择。况且在Flutter中,开发者面对的只有一个主Isolate,在Isolate中可以通过事件队列来实现异步(网络请求、文件IO)等。
1.1.4 说明
1.为了达到设计目的,采取隔离设计去掉锁的应用
2.锁去掉后,线程间的信息通信需要建立通信机制来完成,参考linux os 底层策略上来讲本质上就是走IO那套体系来完成
1.2 Isolate本质
1.1.1 图例
1.1.2 说明
Dart本身抽象了isolate和thread,实际上底层还是使用操作系统的提供的OSThread。
1.3 Isolate组成
1.3.1 Stack用于存放函数调用上下文和调用链路信息。
1.3.2 Heap用于存放对象,堆内存回收管理和java类似。
1.3.3 Queue用于存放异步回调,分为微事件队列(MicroTaskQueue)和微任务队列(EventQueue)。
1.3.4 EventLoop用于处理异步回调。
1.4 Isolate底层逻辑
1.4.1 说明
虽然在内存表现上,Isolate内存隔离性像是进程的特点。但是从实现上不可能把Isolate作为一个进程,因为进程太重了,每新建一个进程,内核系统都会为新进程创建独立的虚拟内存,保存进程相关的数据结构,并且进程切换效率比较低。所以从可行性上来说Isolate的本质应该是一个线程。也就是说Isolate是通过线程实现的。我们使用多个Isolate也就是使用多个线程,只不过与传统线程不同的是,Isolate之间内存不共享,但可以通过通信机制互通。
1.4.2 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中内存分配模型,和高效垃圾的回收机制。
1.5 运行图例
1.6 Isolate模式
在dart中编写的代码分为两种类型
1.6.1 同步模式
即正常编写的代码。
BIO模式
1.6.2 异步模式
一些返回类型为Future和Stream的函数。
NIO模式
1.7 Isolate使用案例
1.7.1 常规使用
-
import 'dart:isolate';
-
void main(){
-
print("main begin");
-
Isolate.spawn((message) {
-
print("匿名函数线程:$message");
-
}, "inner msg...");
-
Isolate.spawn(newThread1, "hello 1");
-
Isolate.spawn(newThread2, "hello 2");
-
Isolate.spawn(newThread3, "hello 3");
-
print("main end");
-
}
-
void newThread1(String msg){
-
print("Thread 1 msg:$msg");
-
}
-
void newThread2(String msg){
-
print("Thread 2 msg:$msg");
-
}
-
void newThread3(String msg){
-
print("Thread 3 msg:$msg");
-
}
1.7.2 isolate.spwanUri
-
//当前文件
-
-
import 'dart:isolate';
-
///isolate spawnUri方式
-
void main() {
-
print("main begin");
-
test1();
-
newThread();
-
test2();
-
print("main end");
-
}
-
void test1(){
-
print("test1.....");
-
}
-
void test2(){
-
print("test2.....");
-
}
-
///参数的定义可以随意,参数中接收的SendPort是需要通信的发送端口
-
-
void newThread(){
-
print("新线程.....");
-
ReceivePort receivePort = ReceivePort();
-
SendPort sendPort = receivePort.sendPort;
-
Isolate.spawnUri(Uri(path: "./isolate_spawnUri_task.dart"),["msg1","msg2","msg3"], sendPort);
-
//监听
-
receivePort.listen((message) {
-
print("主线程接收到来自子线程消息:${message}");
-
switch(message[1]){
-
case 0:
-
//子线程正在处理初始化数据...
-
print("接收到初始化消息");
-
break;
-
case 1:
-
//子线程异步数据正在处理中..
-
print("接收到处理中状态消息");
-
break;
-
case 2:
-
//子线程数据处理完整
-
print("接收到任务完成消息");
-
receivePort.close();
-
break;
-
}
-
});
-
}
-
-
-
//目标文件
-
-
import 'dart:isolate';
-
import 'dart:io';
-
///isolate spawnUri方式
-
void main(List<String> args,SendPort sendPort) {
-
print("isolate_spawnUri_task.dart begin");
-
print("接收到相关参数:$args");
-
sendPort.send(["开始执行异步操作",0]);
-
sleep(Duration(seconds: 2));
-
sendPort.send(["加载数据中...",1]);
-
sleep(Duration(seconds: 2));
-
sendPort.send(["异步任务完成",2]);
-
sleep(Duration(seconds: 2));
-
print("isolate_spawnUri_task.dart end");
-
}
1.8 Isolate通信
1.8.1 说明
创建Isolate时需要指定一个接收端口(ReceivePort)的发送端口(SendPort),调用者可以通过这个发送端口发送数据到其他的Isolate中ReceivePort的listen中,这种机制被称为消息传递(message passing)
既然是内存隔离的,那么在调用者所在Isolate发送的消息数据是怎么传递到接收者所在的Isolate中的呢?也就是说Isolate通信的底层逻辑是什么呢?
答案是map_变量,map_是一个Hash列表。是在Dart虚拟启动时初始化的,所以map_变量是存在于Dart虚拟机所属内存的,而这块内存是各个Isolate共享的。
1.8.2 通信
-
import 'dart:isolate';
-
-
///isolate 通信 - 单向
-
void main() async{
-
print("main begin");
-
-
ReceivePort receivePort = ReceivePort();
-
Isolate.spawn(newThread,["你好",receivePort.sendPort]);
-
//方案1:通过listen进行监听
-
// receivePort.listen((message) {
-
// print("listen方式-主线程接收到消息:$message");
-
// });
-
//方案2:通过 await关键字与async关键字建立阻塞通道
-
var msg = await receivePort.first;
-
//上面这两种方案上只选选择一种处理
-
//原因:这里dart对于数据的等待接收,我们可以看做为socket的BIO与NIO
-
//listen方案实际上就是利用select,epoll这种方案在进行循环监听
-
//await 就是read的阻塞式等待
-
-
-
print("await方式-主线程接收到消息:$msg");
-
print("main end");
-
}
-
-
-
///参数的定义可以随意,参数中接收的SendPort是需要通信的发送端口
-
///@sendPort
-
void newThread(var message){
-
String msg = message[0];
-
SendPort sendPort = message[1];
-
print("通过参数传递的数据1:$msg");
-
-
//通过传递过来的sendPort给主线程回消息
-
sendPort.send("这个是子线程给主线程回的消息!!!");
-
}
1.8.3 为什么将Isolate设计成隔离的
1、首先说目前由移动端页面(包含Android、iOS、Web)构建的特性—树形结构构建布局、布局解析抽象、绘制、渲染,这一系列的复杂步骤导致必须在同一个线程完成。**因为多线程操作页面UI元素会有并发的问题,有并发就必须要加锁,加锁就会降低执行效率。所以强制在同一线程中操作UI是最好的选择。**况且在Flutter中,开发者面对的只有一个主Isolate,在Isolate中可以通过事件队列来实现异步(网络请求、文件IO)等。所以不需要再使用其他线程完成异步。
2、每当有页面交互时,必定会引起布局变化而重新绘制,这个过程会有频繁的大量的UI控件的创建和销毁,这就涉及到了耗时内存分配和回收。而这些较短生命周期的对象是存放在堆内存的新生代的,当虚拟机回收新生代内存时是要stop the world的,在Android或iOS中,各个线程共用一块堆内存,当非UI线程频繁申请、释放内存时也会触发垃圾回收,所以会间接影响UI线程的运行。
Dart为了解决这个问题,就每个Isolate(看做线程)分配各自的一块堆内存,并且独自管理内。这样的策略使得内存的分配和回收变得简单高效,并且不受其他Isolate的影响。
1.9 总结
1、Dart中向应用层提供了线程的封装——Isolate。应用层是不能创建线程的,只能使用Isolate
2、Isolate与传统的线程不同的是,内存隔离
3、Isolate设计成隔离的,是出于移动端页面UI构建特性考虑。第一点,UI绘制必须在同一线程内完成,所以强制同一线程是最好的选择。第二点,传统的线程内存共享,其他线程频繁的申请释放内存会触发垃圾回收,间接影响UI线程运行
2 Dart io
-
Future<void> main() async {
-
var config = File('config.txt');
-
// Put the whole file in a single string.
-
var stringContents = await config.readAsString();
-
print('The file is ${stringContents.length} characters long.');
-
// Put each line of the file into its own string.
-
var lines = await config.readAsLines();
-
print('The file is ${lines.length} lines long.');
-
}
readAsBytes()
的调用返回一个Future,该函数在可用时提供结果-
Future<void> main() async {
-
var config = File('config.txt');
-
var contents = await config.readAsBytes();
-
print('The file is ${contents.length} bytes long.');
-
}
-
Future<void> main() async {
-
var config = File('config.txt');
-
try {
-
var contents = await config.readAsString();
-
print(contents);
-
} catch (e) {
-
print(e);
-
}
-
}
-
import 'dart:io';
-
import 'dart:convert';
-
-
Future<void> main() async {
-
var config = File('config.txt');
-
Stream<List<int>> inputStream = config.openRead();
-
var lines = utf8.decoder.bind(inputStream).transform(const LineSplitter());
-
try {
-
await for (final line in lines) {
-
print('Got ${line.length} characters from stream');
-
}
-
print('file is now closed');
-
} catch (e) {
-
print(e);
-
}
-
}
-
var logFile = File('log.txt');
-
var sink = logFile.openWrite();
-
sink.write('FILE ACCESSED ${DateTime.now()}\n');
-
await sink.flush();
-
await sink.close();
var sink = logFile.openWrite(mode: FileMode.append)
add(List<int> data)
。-
Future<void> main() async {
-
var dir = Directory('tmp');
-
try {
-
var dirList = dir.list();
-
await for (final FileSystemEntity f in dirList) {
-
if (f is File) {
-
print('Found file ${f.path}');
-
} else if (f is Directory) {
-
print('Found dir ${f.path}');
-
}
-
}
-
} catch (e) {
-
print(e.toString());
-
}
-
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgcbbgc
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01