Flutter线程模型

事件循环在UI框架里面应该算是一个常见的东西,例如安卓主线程里面就有个Looper一直在MessageQueue里面读取事件。Flutter里面也有类似的东西。

实际上Flutter的事件循环应该是Dart语言层面就支持的东西。Dart是单线程模型的编程语言,它的一个线程对应一个Isolate,而一个Isolate就会带有一个事件循环。值得注意的是虽然你可以启动多个Isolate来实现多线程,但是正如它的名字"隔离",Isolate之间是内存隔离的,它们有独立的堆内存,这意味着两个Isolate不会出现线程安全问题:

Isolate启动之后就会默认开启事件循环,并不需要我们干些什么。这里有个比较有意思的事情是,不像其他语言,main函数执行完成之后整个程序就结束并退出了。Dart的main函数执行完之后会进入事件循环,监听事件并且进行回调,这些事件包括按键事件、Timer事件等:

所以从这个角度看,Dart的main函数更像是一个init函数,它意味着Dart程序的启动,但它退出后Dart程序还会进行运行。例如下面的代码:

void main() {
  print("main begin");
  Timer(Duration(seconds: 3), () {
    print("on timer");
  });
  print("main begin finish");
}

通过打印我们可以看到,就算main函数退出了,等3秒后Timer时间到达依然会打印"on timer":

I/flutter (15551): main begin
I/flutter (15551): main begin finish
I/flutter (15551): on timer

消息队列

消息循环一般和消息队列配套使用,就例如安卓里面的Looper和MessageQueue。而Dart里面有两条消息队列microtask队列和event队列,时间循环会执行microtask队列内的任务,microtask队列为空之后再去执行event队列里面的任务:

event队列包含Dart和来自系统其它位置的事件。但microtask队列只包含来自当前isolate的内部代码。如果我们希望在这次事件循环处理完成之后,下一次事件循环处理之前做一些事情,就可以往microtask队列里面加入任务:

void main() {
  new Future(() {
    scheduleMicrotask(()=>print("in microtask queue 1"));
    print("in event queue 1");
  });
  new Future(() {
    scheduleMicrotask(()=>print("in microtask queue 2"));
    print("in event queue 2");
  });
  new Future(() {
    scheduleMicrotask(()=>print("in microtask queue 3"));
    print("in event queue 3");
  });
}

在main函数里面我们使用Future往event队列插入了3个task,但是执行到每个task的时候,又会往microtask队列插入microtask,这就造成task执行完之后重新遍历microtask队列发现microtask去执行:

in event queue 1
in microtask queue 1
in event queue 2
in microtask queue 2
in event queue 3
in microtask queue 3

Future有个then方法,可以在Future执行完之后执行指定操作:

void main() {
  new Future(() {
    scheduleMicrotask(()=>print("in microtask queue 1"));
    print("in event queue 1");
    return "a";
  }).then((a) {
    scheduleMicrotask(()=>print("in microtask queue 2"));
    print("in event queue 2 -> $a");
    return new Future(() { return "b";});
  }).then((b) {
    scheduleMicrotask(()=>print("in microtask queue 3"));
    print("in event queue 3 -> $b");
  });
}

一开始我理解错误,以为是在这个Future task后面插入另一个Future,其实实际上then里面的是callback,它的参数是上一个callback的返回值。callback会在Future执行完之后立即执行,而不会插入event队列。但如果上一个callback的返回值是Future的话,就会往event队列插入任务,而后面的callback实际上监听的是这个返回的Future。所以上面的代码打印如下:

I/flutter (23893): in event queue 1
I/flutter (23893): in event queue 2 -> a
I/flutter (23893): in microtask queue 1
I/flutter (23893): in microtask queue 2
I/flutter (23893): in event queue 3 -> b
I/flutter (23893): in microtask queue 3

单线程模型

线程阻塞

另外正如一开始说的Dart是单线程模型,由于这个事件循环是在单个线程内的,所以如果我们的task耗时比较长就会阻塞后面的task:

void main() {
  print('start time :' + DateTime.now().toString()); // 当前时间
  Timer(Duration(seconds: 1), () {
    //callback function
    print('first timer :' + DateTime.now().toString()); // 1s之后
    sleep(Duration(seconds: 3));
  });
  Timer(Duration(seconds: 2), () {
    //callback function
    print('second timer :' + DateTime.now().toString()); // 2s之后
  });
}

所以我们的第二个Timer虽然设定是在2秒后执行,但是实际上它会被第一个timer的3秒sleep阻塞住,等到第一个Timer结束才执行:

I/flutter (21196): start time :2021-10-20 21:30:11.794680
I/flutter (21196): first timer :2021-10-20 21:30:12.835643
I/flutter (21196): second timer :2021-10-20 21:30:15.841398

因此我们不能过分的信任这些定时任务。

未捕获的异常

不像java、kotlin这些语言多线程语言,如果一个线程中出现了未捕获的异常,那么这个线程就被强制结束了。由于采用事件循环的机制来运行相对独立的task,Dart不要求我们必须处理异常。当一个task出现了异常,虽然会结束这个task,但是并不影响整个线程,后续的其他task仍可以继续执行:

void main() {
  new Future(() {
    print("task1 begin");
    throw new Exception();
    print("task1 finish");
  });
  new Future(() {
    print("task2 begin");
    print("task2 finish");
  });
}

task1被中断了,但是task2依然会执行:

task1 begin
Error: Exception
    at Object.throw_ [as throw] (http://localhost:64924/dart_sdk.js:5041:11)
    at http://localhost:64924/packages/myflutter/main.dart.lib.js:365:17
    at http://localhost:64924/dart_sdk.js:32040:31
    at internalCallback (http://localhost:64924/dart_sdk.js:24253:11)
task2 begin
task2 finish

在Dart里面实现多线程

虽然通过Dart的async await可以实现类似kotlin的协程的功能,但是如果不做特殊操作,这些协程实际上是跑在同一个线程中的。一旦某个协程做了些耗时操作如复杂计算等,就会造成ui卡顿。这个时候我们可以创建多个Isolate去实现类似多线程的操作:

int globalData = 1;

void otherIsolate(SendPort sendPort) {
  while(true){
    globalData ++;
    sleep(Duration(seconds: 1));
    sendPort.send("globalData from otherIsolate $globalData");
  }
}

void main() {
  globalData = 100;

  ReceivePort receivePort = ReceivePort();
  receivePort.listen((message) {
    print(message);
  });

  Isolate.spawn(otherIsolate, receivePort.sendPort);
  Future.delayed(Duration(seconds: 3), ()=>{
    print("globalData in Future : $globalData")
  });
}

我们可以通过ReceivePort这种类似管道的东西来进行Isolate之间的通信。正如之前所说Isolate是内存隔离的,它更像一个进程的概念。所以并不能像其他语言的多线程一样通过全局变量来交换数据:

I/flutter (17503): globalData from otherIsolate 2
I/flutter (17503): globalData from otherIsolate 3
I/flutter (17503): globalData in Future : 100
I/flutter (17503): globalData from otherIsolate 4
I/flutter (17503): globalData from otherIsolate 5
I/flutter (17503): globalData from otherIsolate 6

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flutter是一种基于Dart编程语言的移动应用开发框架。由于移动应用通常需要处理大量数据和网络请求等复杂任务,因此Flutter对于多线程的支持非常重要。 Flutter提供了Dart的异步编程机制,包括async/await和Future/Promise等。使用这些机制可以在不阻塞应用程序的情况下执行耗时任务。这样做可以提高UI的响应性以及应用程序的用户体验。 Flutter还内置了Isolate,这是Dart中的一个非常强大的多线程工具。Isolate提供了一种在不同进程之间传递数据的方式,可以大大简化多线程编程的复杂度。Flutter中的Isolate可以执行在后台进行耗时任务,例如处理图像或音频等任务,从而提高整个应用程序的性能和响应性。 除了内置的Isolate,Flutter还提供了用于管理和调度多线程任务的工具。例如,Flutter Scheduler可以控制UI和其他后台任务的执行时间,以确保应用程序的资源得到有效的利用。Flutter中的并行任务也可以基于线程池执行,这将更好地分配任务和管理资源。 总之,Flutter提供了多种方法来处理复杂的任务,并在多线程编程方面提供了重要的支持。同时,开发人员还应该理解多线程编程的挑战和最佳实践,并根据应用程序的需求进行合理的设计和实现。 ### 回答2: Flutter提供了许多不同的方式来执行多线程操作,包括像Isolate和Compute这样的API,以及使用Dart语言中的Async / Await关键字执行异步操作的选项。 其中,Isolate是Dart语言中的一种轻量级线模型,它允许我们在代码中同时运行多个任务而不会阻塞主线程。使用Isolate,我们可以并发地运行多个任务,通过消息通信在它们之间传递数据。它们之间相互独立,可避免在一个线程中执行长时间运行的操作影响应用程序响应性能。 与Isolate不同,Compute API是Flutter框架提供的方法。如Isolate API,开发人员可以通过Compute通过将函数作为参数传递来创建并行计算。每个行为都会在自己的Isolate中执行,因此它们之间不会影响对资源的访问。此外,Flutter还提供了Future和Async / Await的方式来进行任务的并行处理和异步代码的管理。 综上所述,Flutter提供了多种执行多线程操作的方式,包括Isolate和Compute API、Future和Async / Await等。在实际开发中,我们应根据具体需求选择合适的方式来进行多线程操作,提高应用程序的响应性能和并行计算的速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值