前言
应用中的并发优化就是在响应用户操作期间,尽可能地让主线程只执行UI绘制相关的任务,而将非UI的耗时任务分配给其他线程或者延迟处理。这样借助多线程的异步技术,充分利用多核处理器的能力,提高应用程序的并发处理能力,减少用户等待时间,保证用户界面的响应流畅性。
ArkTS为我们提供了TaskPool与Worker两种多线程并发方案。
(小tips:鸿蒙是多线程噢!!)
一、TaskPool和Worker工作原理
TaskPool与Worker两种多线程并发能力均是基于 Actor并发模型实现的。 Worker主、子线程通过收发消息进行通信;TaskPool基于Worker做了更多场景化的功能封装
1.Worker工作原理
Worker主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞主线程的运行。
工作原理如下图所示:
在多核的情况下(下图中的CPU 1和CPU 2同时工作),多个Worker线程(下图中的Worker thread1和Worker thread2)可以同时执行。
2.TaskPool工作原理
任务池(TaskPool)作用是为应用程序提供一个多线程的运行环境,降低整体资源的消耗、提高系统的整体性能,且您无需关心线程实例的生命周期。
工作原理如下图:
注意:两种多线程方法内存都是不共享的!
二、使用方法
1.taskPool的用法
注意事项:
- 实现任务的函数需要使用**@Concurrent装饰器标注,且仅支持在.ets文件中使用,还必须是一个全局函数**。
- (重点)多线程绝对不允许闭包代码出现!!!!
- taskPool的函数几乎不能使用的第三方自己编写的工具(原因如上)
- taskPool是由操作系统调度,可以设置优先级,默认级别是中等,不需要开发者占用
- taskPool是直接传递参数
绝对不允许的代码如下(示例):
function bar() {
}
@Concurrent
function foo() {
bar(); // 违反闭包原则,报错
}
taskpool使用示例:
@Concurrent
function loopMillion() {
}
使用方式一:
taskpool.execute(loopMillion) // 开启多线程
使用方式二:
const task1 = new taskpool.Task(loopMillion)
taskpool.execute(task1, taskpool.Priority.HIGH)
值得一提的是,taskPool执行完成会在操作系统控制下自焚,不需要我们销毁。
2.Worker的用法
注意事项:
- Worker创建后需要手动管理生命周期(需要开发者手动关闭),且最多同时运行的Worker子线程数量为64个。
- workder是通过onmessage和postMessage发消息实现主线程和子线程的通信的
创建方式:
3.注意事项
taskPool和worker不能接受被 -State/Prop/Link 修饰的参数 假如一定想传递这种参数- let a = [...this.banners]
总结
TaskPool与Worker是性能优化的两种方式,结合我们开发需求进行选择。