一、核心设计模式:Worker 作为常驻调度中心
- 在 HarmonyOS Next 中,将常驻线程 Worker 与高并发任务池 TaskPool 结合,是处理复杂、高性能后台任务的最佳实践。这种"统一调度,分布执行"的模式,能有效解决单一 Worker 难以扩展,或纯 TaskPool 无法维护状态的难题。
- HarmonyOS Next 使用 Worker 作为常驻调度中心,该架构的核心,是让一个常驻的 Worker 线程充当调度中心(Dispatcher) 的角色。
- 生命周期管理:Worker 线程不受 TaskPool 的 3 分钟同步任务时长限制,适合长期存活,它独立创建,并需开发者调用 terminate() 手动清理,以精确控制其生命周期。
- 职责分工:
-
- 主线程:负责下发任务。将任务请求发送给 Worker 调度中心,自身保持界面响应流畅。
-
- Worker 调度中心:接收主线程发来的各种任务请求,负责维护任务队列和状态。
-
- TaskPool 执行器:Worker 内部通过 taskpool.execute() 将具体任务分发给系统的全局线程池执行,并利用 Promise 获取返回结果。
- 消息通信机制:所有通信均基于结构化克隆算法,主线程与 Worker 之间通过 postMessage 和 onmessage 传递命令与最终聚合结果;而 Worker 调度中心与 TaskPool 执行器之间,则通过 Promise 对象进行直接的异步交互。
二、架构实现:详细流程与代码示例
- 我们可以通过一个具体的场景来说明。假设我们需要实现以下三步后台处理流程:
-
- 用户上传一批本地图片;
-
- 后台为每张图片生成一个唯一ID和存储路径;
-
- 最后,统一将这批图片上传到服务器。
- 定义并发任务函数(Task):
javascript
// entry/src/main/ets/tasks/ImageTask.ets
import { taskpool } from '@kit.ArkTS';
// 定义上传图片的具体任务
@Concurrent
async uploadImage(imagePath: string): Promise<string> {
// 模拟异步上传操作,此处应为实际的网络请求
await new Promise<void>((resolve) => setTimeout(() => resolve(), 500));
console.info(`图片 ${imagePath} 上传成功`);
return `https://cdn.com/${imagePath}`;
}
// 将函数导出,供Worker调用
export { uploadImage };
- 创建常驻 Worker 调度中心:
javascript
// entry/src/main/ets/workers/ImageWorker.ets
import { worker, taskpool } from '@kit.ArkTS';
import { uploadImage } from '../tasks/ImageTask'; // 导入任务
const workerPort = worker.workerPort;
// 任务队列,用于并发控制
class TaskQueue {
private maxConcurrent = 3;
private running = 0;
private queue: (() => Promise<void>)[] = [];
public add(task: () => Promise<void>) {
this.queue.push(task);
this.run();
}
private async run() {
if (this.running >= this.maxConcurrent) return;
if (this.queue.length === 0) return;
this.running++;
const task = this.queue.shift();
await task?.();
this.running--;
this.run();
}
}
const queue = new TaskQueue();
// 监听主线程发来的任务请求
workerPort.onmessage = async (event: MessageEvent) => {
const images = event.data;
console.info(`[Worker] 收到 ${images.length} 张图片,开始调度...`);
const uploadResults: string[] = [];
// 将任务提交至TaskPool,并由队列管理并发
for (const img of images) {
const p = taskpool.execute(uploadImage, img);
queue.add(async () => {
const url = await p;
uploadResults.push(url);
});
}
// 等待所有任务完成后,将最终结果返回给主线程
// 注意:使用taskpool.TaskGroup进行简单批量等待,代码会更简洁
const taskGroup = new taskpool.TaskGroup();
images.forEach(img => taskGroup.addTask(new taskpool.Task(uploadImage, img)));
try {
const results = await taskpool.execute(taskGroup);
workerPort.postMessage(results);
} catch (error) {
workerPort.postMessage({ error: `上传失败: ${error.message}` });
}
};
- 主线程发起任务:
javascript
// entry/src/main/ets/pages/Index.ets
import { worker } from '@kit.ArkTS';
@Entry
@Component
struct Index {
private imageWorker?: worker.ThreadWorker;
aboutToAppear() {
// 创建Worker实例
this.imageWorker = new worker.ThreadWorker('entry/ets/workers/ImageWorker.ets');
this.imageWorker.onmessage = (event: MessageEvent) => {
if (event.data.error) {
console.error(`任务失败: ${event.data.error}`);
} else {
console.info(`所有图片上传成功,URL列表: ${JSON.stringify(event.data)}`);
}
};
}
build() {
Column() {
Button('开始处理图片')
.onClick(() => {
// 模拟本地图片路径列表
const images = ['/images/1.jpg', '/images/2.jpg', '/images/3.jpg'];
this.imageWorker?.postMessage(images);
})
}
.width('100%')
.height('100%')
}
aboutToDisappear() {
this.imageWorker?.terminate(); // 页面销毁时,释放 Worker 资源
}
}
三、架构优势与核心关键点
- 采用这种混合架构优势明显:通过 Worker 管理上下文,TaskPool 执行无状态任务,实现了职责分离,使代码更易维护;TaskPool 能自动负载均衡并高效复用线程,最大化利用系统资源;同时 Worker 作为单一入口与主线程通信,有效简化了多线程下的通信与状态同步复杂度。
- 注意事项:
-
- 文件配置:创建 Worker 文件后,必须在项目的 build-profile.json5 中进行配置,确保其能被正确打包。
-
- 应用退出:应用退出时,系统一般会回收所有相关资源,但Worker的显式终止(terminate)依然是最佳实践。
-
- 数据传输:postMessage 在传输大对象时会进行序列化/反序列化,带来性能开销。对于大数据场景,优先使用 ArrayBuffer 转移或 SharedArrayBuffer 来避免拷贝;
-
- 错误与调试:务必为 Worker 注册 onerror 回调,防止线程异常时应用崩溃。在 DevEco Studio 中,可使用其并发分析器监控 TaskPool 队列状态和 Worker 内存占用,以优化任务划分阈值。