Flutter中并发的实现与理解

一,Isolate的理解

Isolate可以理解为dart中的线程,但它又不同于线程,准确的说应该叫做协程,协程最大的优势就是它具有极高的执行效率,因为协程中子程序的调用不需要线程的切换,所以对于线程数量越大的程序来说协程的优势就越明显。每个isolate都有自己独立的执行线程和事件循环,以及内存,所以isolate之间不存在锁竞争的问题,各isolate之间通过消息通信。

二,单线程设计的目的

移动端页面(包含Android、iOS、Web)构建的特性—树形结构构建布局、布局解析抽象、绘制、渲染,这一系列的复杂步骤导致必须在同一个线程完成。因为多线程操作页面UI元素会有并发的问题,有并发就必须要加锁,加锁就会降低执行效率。所以强制在同一线程中操作UI是最好的选择。

在Flutter中,为了达到设计目的,采取隔离设计去掉锁的应用,锁去掉后,线程间的信息通信需要建立通信机制来完成,开发者面对的只有一个主Isolate,在Isolate中可以通过事件队列来实现异步(网络请求、文件IO)等。

三,Isolate的本质

1,Isolate的组成

在Dart中每一个线程叫做Isolate,每一个isolate中包含一个event loop(事件循环),event queue和microtask queue两个事件队列。

  • Stack用于存放函数调用上下文和调用链路信息

  • Heap用于存放对象,堆内存回收管理和java类似。

  • Queue用于存放异步回调,分为微事件队列(MicroTaskQueue: 其事件的优先级比event queue高,可以在UI更新之前执行一些特定的任务。)和微任务队列(EventQueue)负责处理I/O事件,绘制事件,手势事件,接收其他isolate消息等外部事件。

  • EventLoop用于处理异步回调。

所有的任务在执行时:会先判断microtask queue队列是否为空,如果不为空是,则优先执行microtask queue中的任务,等到microtask queue队列任务执行完成之后,再执行event queue队列里面的任务,直到App退出。


注意:在执行microtask事件时,会阻塞event 队列,可能会出现ui卡顿,为保证渲染的及时性,尽量把耗时任务放在event队列中

2,Isolate底层实现

Dart本身抽象了Isolate和thread,实际上底层还是使用操作系统的提供的OSThread

3,Isolate的内存隔离

在isolate中堆内存主要被分为新生代和老年代两块区域,针对不同的区域采用了不同的垃圾回收策略,新生代采用:复制清除算法,老年代采用:标记清除和标记整理两种算法,来适应不同的内存回收场景,以保证UI的流畅性。

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内存隔离性像是进程的特点。但从实现上不可能把Isolate作为一个进程,因为进程太重了,每新建一个进程,内核系统都会为新进程创建独立的虚拟内存,保存进程相关的数据结构,并且进程切换效率比较低。所以从可行性上来说Isolate的本质应该是一个线程。也就是说Isolate是通过线程实现的。我们使用多个Isolate也就是使用多个线程,只不过与传统线程不同的是,Isolate之间内存不共享,但可以通过通信机制互通。

4,Isolate的通信机制

在Isolate中,每个隔离都有自己的内存和运行事件循环的单个线程。每条隔离消息可以传递一个对象,其中包括可从该对象传递到达的任何内容。

并非所有对象类型都是可发送的,如果任何可传递到达的对象不可发送,则发送会失败。例如,List<Object>仅当列表中没有不可发送的对象时,您才可以发送 类型的对象。如果其中一个对象是 Socket,则发送会失败,因为套接字无法发送。

注:

  • 工作隔离可以执行 I/O(例如读取和写入文件)、设置计时器等。它有自己的内存,

  • 不与主隔离共享任何状态。工作隔离区可以阻塞而不影响其他隔离区。

  • 工作隔离将保存结果的内存传输到主隔离。它不复制数据。工作隔离执行验证过程以确保允许传输对象。

四,Isolate的创建及使用

1,匿名的Isolate

///创建匿名的isolate
Isolate.spawn((message) {
  print("匿名函数线程:$message");
}, "inner msg...");

2,带入参的Isolate

Isolate.spawn(newThread1, "hello 1"); 

void newThread1(String msg){   
    print("Thread 1 msg:$msg"); 
}

3,Isolate.run的使用

Future<List<Photo>> getPhotos() async {
  final String jsonString = await rootBundle.loadString('assets/photos.json');
  final List<Photo> photos = await Isolate.run<List<Photo>>(() {
    final List<Object?> photoData = jsonDecode(jsonString) as List<Object?>;
    return photoData.cast<Map<String, Object?>>().map(Photo.fromJson).toList();
  });
  return photos;
}

4,compute的使用

///使用compute快速创建Isolate
var allAreaList = await compute(FileUtils.readFileContent,file);

///耗时任务
static Future<List<AreaInfo>> readFileContent(File file) async {
  String fileContent = await file.readAsString();
  if (fileContent.isNotEmpty) {
    List<dynamic> list =  json.decode(fileContent);
    return list.map((e) => AreaInfo.fromDBJson(e)).toList();
  }else{
    return [];
  }
}

5,Isolate之间的通信

void main() async{
  final receivePort = ReceivePort();
  await Isolate.spawn(runIsolate,receivePort.sendPort);
  receivePort.listen((message) { 
    print("Received message:$message");
  });
}

void runIsolate(SendPort sendPort){
  print("Isolate running");
  sendPort.send("Message from isolate");
  print("Isolate exiting");
}

注:ReceivePort 和 SendPort 是在Isolate间传递消息的接口。它们在底层通过一种管道机制来实现Isolate之间的通信。

五,isolate的生命周期

Isolate 生命周期的调用链路可以被大致分为创建、启动、执行、挂起和销毁这几个阶段。以下是 Isolate 生命周期的简要描述:

  1. 创建(Creation):

    1. Isolate 的创建是通过调用 Isolate.spawn 或者 Isolate.spawnUri 函数实现的。这个过程会初始化 Isolate 的环境,包括分配内存空间和设置 Isolate 的初始状态。

  2. 启动(Startup):

    1. 一旦 Isolate 被创建,它会开始执行其入口函数。这是 Isolate 生命周期的启动阶段。

  3. 执行(Execution):

    1. 在执行阶段,Isolate 执行其指定的任务或逻辑。这可能是一个长时间运行的操作,例如网络请求、计算密集型任务等。

  4. 挂起(Suspension):

    1. Isolate 可能会在执行过程中挂起,这通常是因为用户在应用程序中切换到了其他页面或者发生了其他需要优先级更高的事件。在挂起期间,Isolate 的执行被暂停,以节省系统资源。

  5. 恢复(Resumption):

    1. 当 Isolate 不再被挂起时,它会恢复执行。这可能是因为用户回到了之前的页面或者其他事件已经处理完毕。

  6. 销毁(Termination):

    1. 当不再需要 Isolate 时,它会被销毁。这可能是因为应用程序退出或者 Isolate 的任务完成。在销毁之前,可以执行一些清理工作,如释放资源、关闭连接等。

注意:Flutter 中的 Isolate 生命周期与应用程序的生命周期是独立的。如果应用程序被销毁,Isolate 不会随之销毁,除非你显式地终止它们。

  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值