大家好,我是simple。我的理想是利用科技手段来解决生活中遇到的各种问题。
今天跟大家一起探讨一下鸿蒙开发的多线程。ArkTS提供了TaskPool和Worker两种并发能力供开发者选择。
先说个人理解:在轻量、频繁性地需要使用多线程场景使用taskpool,在计算量大、耗时资源大需要使用多线程时使用worker。
概念
TaskPool的主要作用是为应用程序提供一个多线程的运行环境,它支持开发者在宿主线程提交任务到任务队列,系统选择合适的工作线程执行任务,再将结果返回给宿主线程。无需管理线程生命周期。
Worker的主要作用也是为应用程序提供一个多线程的运行环境,满足应用程序在执行过程中与宿主线程分离,在后台线程中运行脚本进行耗时操作,避免计算密集型或高延迟的任务阻塞宿主线程。用完的时候需要手动进行销毁,否则会一直消耗内存。
2. 适用场景
-
Worker:适用于需要长时间运行且相对独立的任务,例如大量数据的处理、复杂的算法计算等。由于
Worker创建的是独立线程,它可以在不影响主线程的情况下,持续进行复杂的计算任务。 -
TaskPool:更适合执行多个短时间的任务,尤其是那些可以并行执行的任务。比如在应用启动时,需要同时初始化多个模块的任务,使用
TaskPool可以将这些任务分配到不同的线程中并行执行,从而提高应用的启动速度。
3. 使用方式
- Worker:创建
Worker需要指定一个脚本文件,在该脚本文件中编写要执行的任务逻辑。主线程和Worker线程之间通过postMessage方法进行通信。以下是一个简单示例:
在模块级entry/build-profile.json5配置文件添加如下配置:
"buildOption": {
"sourceOption": {
"workers": [
"./src/main/ets/workers/Worker.ets"
]
}
}
// 宿主线程中创建Worker对象
const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");
// 宿主线程向worker线程传递信息
workerInstance.postMessage({ data: 'test' });
// 宿主线程接收worker线程信息
workerInstance.onmessage = (e: MessageEvents): void => {
// data:worker线程发送的信息
console.info("main thread data is " + e.data);
// 销毁Worker对象
workerInstance.terminate();
}
// 在调用terminate后,执行onexit
workerInstance.onexit = (code) => {
console.log("main thread terminate");
}
workerInstance.onerror = (err: ErrorEvent) => {
console.log("main error message " + err.message);
}
// Worker.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
// 创建worker线程中与宿主线程通信的对象
const workerPort = worker.workerPort
// worker线程接收宿主线程信息
workerPort.onmessage = (e: MessageEvents): void => {
// data:宿主线程发送的信息
console.info("main thread data is " + e.data);
// worker线程向宿主线程发送信息
workerPort.postMessage('result');
}
// worker线程发生error的回调
workerPort.onerror = (err: ErrorEvent) => {
console.log("worker.ets onerror" + err.message);
}
- TaskPool:使用
TaskPool时,开发者只需定义任务函数,然后将任务提交到TaskPool中即可。TaskPool会自动选择合适的线程来执行任务。以下是一个使用TaskPool的示例:
import { taskpool } from '@kit.ArkTS';
@Concurrent
function add(num1: number, num2: number): number {
return num1 + num2;
}
async function ConcurrentFunc(): Promise<void> {
let task: taskpool.Task = new taskpool.Task(add, 1, 2);
console.info("taskpool.execute(task) result: " + await taskpool.execute(task));
}
4. 资源管理
-
Worker:每个
Worker都会创建一个独立的线程,因此会占用相对较多的系统资源。当Worker任务完成后,如果不手动终止,它会一直占用系统资源。在同一个进程中,理论上最多仅能够配置64个Worker(实际数量根据内存剩余空间所决定) -
TaskPool:
TaskPool通过线程池的方式管理线程,避免了频繁创建和销毁线程所带来的资源开销。线程池中的线程可以被多个任务复用,从而提高了资源的利用率。但是TaskPool最多只能够执行3分钟(通常来说这个时间已足够,因为这个时长并不包括异步等待的耗时)。
总结
Worker 通过创建独立线程运行指定脚本,适合长时间且独立的任务,需手动管理线程资源并通过消息通信,而 TaskPool 作为线程池管理工具,自动管理线程的创建、复用和销毁,支持多种任务类型,适合多个短时间并行任务。
文章及代码使用版本为:HarmonyOS 5.0.1 Release SDK。