} finally {
isolate.addOnExitListener(receivePort.sendPort,
response: “isolate has been killed”);
}
isolate?.kill();
}
我们先创建出 ReceivePort,然后在 Isolate.spawn
的时候将 receivePort.sendPort
作为 message 传入新的 isolate。
然后监听 receivePort,并打印收听到的 message。这里需要注意的是,我们需要手动调用 isolate?.kill()
来关闭这个 isolate。
输出结果:
flutter: Isolate Created!
flutter: isolate has been killed
实际上这里不写 isolate?.kill() 也会在 gc 时自动销毁 isolate。
这时候你可能会问,我们的 entryPoint
只允许有一个入参,如果我们想要执行的方法需要传入其他参数怎么办呢。
定义协议
其实很简单,我们定义一个协议就行了。比如像下面这样我们定义一个 SpawnMessageProtocol
作为 message。
class SpawnMessageProtocol{
final SendPort sendPort;
final String url;
SpawnMessageProtocol(this.sendPort, this.url);
}
协议中包含 SendPort 即可。
更方便的 Compute
刚才我们使用的 Isolate.spawn
创建 Isolate 自然会觉得太过复杂,有没有一种更好的方式呢。实际上 Flutter 已经为我们封装了一些实用方法,让我们能够更加自然地使用多线程进行处理。这里我们先创建一个需要在其他 isolate 中运行的方法。
static int _doSomething(int i) {
return i + 1;
}
然后使用 compute 在另一个 isolate 中执行该方法,并返回结果。
runComputeIsolate() async{
int i = await compute(_doSomething, 8);
print(i);
}
仅仅一行代码我们就能够让 _doSomething
运行在另一个 isolate 中,并返回结果。这种方式对使用者来说几乎没有负担,基本上和写异步代码是一样的。
代价是什么
对于我们来说,其实是把多线程当做一种计算资源来使用的。我们可以通过创建新的 isolate 计算 heavy work,从而减轻 UI 线程的负担。但是这样做的代价是什么呢?
时间
通常来说,当我们使用多线程计算的时候,整个计算的时间会比单线程要多,额外的耗时是什么呢?
- 创建 Isolate
- Copy Message
当我们按照上面的代码执行一段多线程代码时,经历了 isolate 的创建以及销毁过程。下面是一种我们在解析 json 中这样编写代码可能的方式。
static BSModel toBSModel(String json){}
parsingModelList(List jsonList) async{
for(var model in jsonList){
BSModel m = await compute(toBSModel, model);
}
}
在解析 json 的时候,我们可能通过 compute 把解析任务放在新的 isolate 中完成,然后把值传过来。这时候我们会发现,整个解析会变得异常的慢。这是由于我们每次创建 BSModel
的时候都经历了一次 isolate 的创建以及销毁过程。这将会耗费约 50-150ms 的时间。
在这之中,我们传递 data 也经历了 Network -> Main Isolate -> New Isolate (result) -> Main Isolate,多出来两次 copy 的操作。如果我们是在 Main 线程之外的 isolate 下载的数据,那么就可以直接在该线程进行解析,最后只需要传回 Main Isolate 即可,省下了一次 copy 操作。(Network -> New Isolate (result)-> Main Isolate)
空间
Isolate 实际上是比较重的,每当我们创建出来一个新的 Isolate 至少需要 2mb 左右的空间甚至更多,取决于我们具体 isolate 的用途。
OOM 风险
我们可能会使用 message 传递 data 或 file。而实际上我们传递的 message 是经历了一次 copy 过程的,这其实就可能存在着 OOM 的风险。
如果说我们想要返回一个 2GB 的 data,在 iPhone X(3GB ram)上,我们是无法完成 message 的传递操作的。
Tips
上面已经介绍了使用 isolate 进行多线程操作会有一些额外的 cost,那么是否可以通过一些手段减少这些消耗呢。我个人建议从两个方向上入手。
- 减少 isolate 创建所带来的消耗。
- 减少 message copy 次数,以及大小。
使用 LoadBalancer
如何减少 isolate 创建所带来的消耗呢。自然一个想法就是能否创建一个线程池,初始化到那里。当我们需要使用的时候再拿来用就好了。
实际上 dart team 已经为我们写好一个非常实用的 package,其中就包括 LoadBalancer
。
我们现在 pubspec.yaml 中添加 isolate 的依赖。
isolate: ^2.0.2
然后我们可以通过 LoadBalancer
创建出指定个数的 isolate。
Future loadBalancer = LoadBalancer.create(2, IsolateRunner.spawn);
这段代码将会创建出一个 isolate 线程池,并自动实现了负载均衡。
由于 dart 天生支持顶层函数,我们可以在 dart 文件中直接创建这个 LoadBalancer
。下面我们再来看看应该如何使用 LoadBalancer
中的 isolate。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
由于篇幅原因,这份面试宝典已经被整理成了PDF文档,有需要Android面试宝典全套完整文档的麻烦点赞+点击GitHub即可获取资料免费领取方式!
本文在开源项目:GitHub中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
链图片转存中…(img-W8qR7pWA-1711393053264)]
本文在开源项目:GitHub中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…