HarmonyOS @ohos.taskpool(启动任务池)完整使用指南
前言
在 HarmonyOS 应用开发中,当我们需要执行耗时操作(如文件读写、数据处理、图片解码等)时,如果在主线程中直接执行,会导致 UI 卡顿甚至 ANR(Application Not Responding)。TaskPool(任务池) 正是为解决这一问题而设计的多线程方案。
本文将系统讲解 @ohos.taskpool 的核心概念、API 用法和最佳实践,并通过一个完整的实战示例帮助你快速上手。
效果

一、TaskPool 概述
1.1 什么是 TaskPool
TaskPool(任务池)为应用程序提供一个多线程运行环境,具有以下特点:
- 降低资源消耗:复用工作线程,避免频繁创建和销毁线程的开销
- 自动负载均衡:根据任务数量动态扩缩容工作线程
- 无需管理生命周期:开发者只需关注任务本身,无需关心线程的创建与回收
- 支持任务取消:可以在任务执行前或执行中尝试取消
1.2 TaskPool vs Worker
| 对比维度 | TaskPool | Worker |
|---|---|---|
| 线程管理 | 系统自动管理 | 开发者手动创建和销毁 |
| 适用场景 | 独立的、短时间的耗时任务 | 需要长期运行的后台线程 |
| 通信方式 | 通过 Promise 返回结果 | 通过 postMessage 通信 |
| 任务取消 | 支持 | 不支持 |
| 并发数量 | 系统动态调度 | 受限于创建的 Worker 数量 |
1.3 适用场景
- 文件读写操作(如批量查询、文件复制)
- 图片/视频解码
- 数据排序、过滤等计算密集型操作
- 关系型数据库查询
- 网络数据解析
二、环境准备
2.1 导入模块
typescript
import { taskpool } from '@kit.ArkTS';
2.2 SDK 版本要求
- 首批接口从 API version 9 开始支持
TaskGroup从 API version 10 开始支持GenericsTask(泛型任务)从 API version 13 开始支持
三、核心概念详解
3.1 @Concurrent 装饰器
@Concurrent 是 TaskPool 的灵魂。所有在任务池中执行的函数必须 使用 @Concurrent 装饰器标记。
基本规则:
- 只能装饰顶层函数(不能是类方法或闭包)
- 参数和返回值必须是可序列化类型
- 函数内部不可访问 UI 上下文(如
getContext) - 函数内部不可访问 外部非
@Concurrent模块的状态
可序列化的数据类型包括:
| 类型类别 | 具体类型 |
|---|---|
| 基本类型 | number, string, boolean, null, undefined |
| 日期 | Date |
| 正则 | RegExp |
| 集合 | Array, Map, Set |
| 二进制 | ArrayBuffer, TypedArray |
| 对象 | 普通 Object(属性值也必须是可序列化类型) |
基础示例:
typescript
@Concurrent
function calculateSum(start: number, end: number): number {
let sum: number = 0;
for (let i: number = start; i <= end; i++) {
sum += i;
}
return sum;
}
常见错误:
错误码 10200014: The function is not marked as concurrent.
出现此错误说明传入 taskpool.execute 的函数没有使用 @Concurrent 标记。
3.2 Priority 优先级
TaskPool 提供三个优先级等级:
| 枚举值 | 数值 | 说明 |
|---|---|---|
taskpool.Priority.HIGH |
0 | 高优先级,优先调度 |
taskpool.Priority.MEDIUM |
1 | 中优先级(默认值) |
taskpool.Priority.LOW |
2 | 低优先级,最后调度 |
typescript
// 不同优先级的任务
taskpool.execute(task1, taskpool.Priority.HIGH); // 高优先级
taskpool.execute(task2, taskpool.Priority.MEDIUM); // 中优先级(默认)
taskpool.execute(task3, taskpool.Priority.LOW); // 低优先级
3.3 Task 任务对象
Task 是 TaskPool 中任务的载体,通过 new taskpool.Task() 创建:
typescript
@Concurrent
function processData(input: string): string {
return input.toUpperCase();
}
// 创建任务
let task: taskpool.Task = new taskpool.Task(processData, "hello world");
// 执行任务(可设置优先级,可取消)
taskpool.execute(task, taskpool.Priority.MEDIUM).then((result: Object) => {
console.info("结果: " + result); // 输出: HELLO WORLD
});
3.4 TaskGroup 任务组
当需要批量执行一组关联任务时,使用 TaskGroup:
typescript
@Concurrent
function square(n: number): number {
return n * n;
}
let group: taskpool.TaskGroup = new taskpool.TaskGroup();
group.addTask(square, 10);
group.addTask(square, 20);
group.addTask(square, 30);
taskpool.execute(group).then((results: Object[]) => {
console.info("结果数组: " + results); // 输出: 100,400,900
});
四、API 详解与示例
4.1 execute(func, ...args) --- 直接执行函数
最简单的用法,直接传入 @Concurrent 函数。此模式不可取消任务。
typescript
@Concurrent
function fibonacci(n: number): number {
if (n <= 1) return n;
let a: number = 0, b: number = 1;
for (let i: number = 2; i <= n; i++) {
let temp: number = a + b;
a = b;
b = temp;
}
return b;
}
// 方式一:Promise 链式调用
taskpool.execute(fibonacci, 40).then((result: Object) => {
console.info("Fibonacci(40) = " + result);
});
// 方式二:async/await
async function runFibonacci(): Promise<void> {
let result: Object = await taskpool.execute(fibonacci, 40);
console.info("Fibonacci(40) = " + result);
}
4.2 execute(task, priority) --- 执行 Task 对象
通过 Task 对象创建并执行任务,可以设置优先级 ,可以取消。
typescript
@Concurrent
function sortArray(arr: Array<number>): Array<number> {
for (let i: number = 0; i < arr.length - 1; i++) {
for (let j: number = i + 1; j < arr.length; j++) {
if (arr[j] < arr[i]) {
let temp: number = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
let data: Array<number> = [5, 3, 8, 1, 9, 2, 7, 4, 6, 0];
let task: taskpool.Task = new taskpool.Task(sortArray, data);
taskpool.execute(task, taskpool.Priority.HIGH).then((result: Object) => {
let sorted: Array<number> = result as Array<number>;
console.info("排序结果: " + sorted);
});
4.3 execute(group, priority) --- 执行任务组
将多个任务打包为一组,全部完成后统一返回结果。
typescript
@Concurrent
function fetchConfig(key: string): string {
// 模拟配置读取
let configs: Record<string, string> = {
"theme": "dark",
"language": "zh-CN",
"fontSize": "16"
};
return configs[key] ?? "unknown";
}
let group: taskpool.TaskGroup = new taskpool.TaskGroup();
group.addTask(fetchConfig, "theme");
group.addTask(fetchConfig, "language");
group.addTask(fetchConfig, "fontSize");
taskpool.execute(group, taskpool.Priority.MEDIUM).then((results: Object[]) => {
for (let i: number = 0; i < results.length; i++) {
console.info("配置项 " + i + ": " + results[i]);
}
});
4.4 cancel(task) --- 取消任务
取消正在排队或正在执行的任务。需要在 @Concurrent 函数内部通过 taskpool.Task.isCanceled() 检查取消状态。
typescript
@Concurrent
function longRunningTask(total: number): number {
let sum: number = 0;
for (let i: number = 0; i < total; i++) {
// 每个迭代检查是否被取消
if (taskpool.Task.isCanceled()) {
console.info("任务在第 " + i + " 次迭代时被取消");
return sum; // 提前退出
}
sum += i;
// 模拟耗时操作
let start: number = Date.now();
while (Date.now() - start < 10) {
// busy wait 10ms
}
}
return sum;
}
let task: taskpool.Task = new taskpool.Task(longRunningTask, 10000);
taskpool.execute(task).then((result: Object) => {
console.info("任务完成,结果: " + result);
});
// 1 秒后取消任务
setTimeout(() => {
taskpool.cancel(task);
console.info("已发送取消请求");
}, 1000);
关键要点:
cancel只发送取消信号,不会强制终止线程- 必须在
@Concurrent函数内部主动调用taskpool.Task.isCanceled()检查 - 建议在循环、耗时操作的迭代中加入检查点
4.5 Task.isCanceled() --- 取消状态检查
isCanceled() 是一个静态方法,用于在 @Concurrent 函数内部检查当前任务是否已被取消。
typescript
@Concurrent
function processWithCheckpoints(dataSize: number): string {
// 检查点 1:任务开始
if (taskpool.Task.isCanceled()) {
return "任务在开始前被取消";
}
// 第一阶段处理
let phase1: string = "第一阶段完成";
// 检查点 2:第一阶段完成后
if (taskpool.Task.isCanceled()) {
return phase1 + ",任务在第二阶段前被取消";
}
// 第二阶段处理
let start: number = Date.now();
while (Date.now() - start < 2000) {
if (taskpool.Task.isCanceled()) {
return phase1 + ",任务在第二阶段中被取消";
}
}
return "全部处理完成";
}
五、进阶用法
5.1 GenericsTask --- 类型安全的任务执行(API 13+)
使用 GenericsTask 可以获得编译期类型检查:
typescript
@Concurrent
function multiply(a: number, b: number): number {
return a * b;
}
// 使用 GenericsTask 获得类型安全
let task: taskpool.Task = new taskpool.GenericsTask<[number, number], number>(multiply, 6, 7);
taskpool.execute<[number, number], number>(task).then((result: number) => {
console.info("6 × 7 = " + result); // 类型安全,result 自动推断为 number
});
5.2 executeDelayed --- 延时执行(API 11+)
typescript
@Concurrent
function delayedJob(): string {
return "延时任务执行完成";
}
let task: taskpool.Task = new taskpool.Task(delayedJob);
// 延迟 2000ms 后执行
taskpool.executeDelayed(2000, task, taskpool.Priority.MEDIUM)
.then((result: Object) => {
console.info("结果: " + result);
});
5.3 异步函数支持
@Concurrent 也可以装饰 async 函数:
typescript
@Concurrent
async function asyncTask(url: string): Promise<string> {
// 在子线程中执行异步操作
let result: string = await new Promise<string>((resolve) => {
setTimeout(() => resolve("从 " + url + " 获取的数据"), 1000);
});
return result;
}
taskpool.execute(asyncTask, "https://api.example.com").then((result: Object) => {
console.info(result);
});
5.4 错误处理
typescript
@Concurrent
function riskyTask(divisor: number): number {
if (divisor === 0) {
throw new Error("除数不能为零");
}
return 100 / divisor;
}
let task: taskpool.Task = new taskpool.Task(riskyTask, 0);
taskpool.execute(task)
.then((result: Object) => {
console.info("结果: " + result);
})
.catch((error: Error) => {
console.error("任务执行出错: " + error.message);
});
常见错误码:
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 401 | 参数错误 | 检查参数类型和数量 |
| 10200006 | 序列化异常 | 确保参数和返回值是可序列化类型 |
| 10200014 | 函数未标记 @Concurrent | 添加 @Concurrent 装饰器 |
| 10200015 | 取消时任务不存在 | 确认任务已被提交到任务池 |
| 10200016 | 取消时任务正在执行 | 在函数内部使用 isCanceled() 检查 |
| 10200051 | 周期任务重复执行 | 长时任务仅支持执行一次 |
六、实战示例:文件统计工具
以下是一个完整的实战示例,演示如何使用 TaskPool 在子线程中统计应用沙箱目录下的文件信息:
typescript
import { taskpool } from '@kit.ArkTS';
import { fileIo } from '@kit.CoreFileKit';
// 定义可序列化的结果接口
interface FileStatResult {
fileName: string;
fileSize: number;
isFile: boolean;
}
@Concurrent
function scanDirectory(dirPath: string): Array<FileStatResult> {
let results: Array<FileStatResult> = [];
let files: Array<string> = fileIo.listFileSync(dirPath);
for (let i: number = 0; i < files.length; i++) {
let filePath: string = dirPath + '/' + files[i];
let stat: fileIo.Stat = fileIo.statSync(filePath);
let item: FileStatResult = {
fileName: files[i],
fileSize: stat.size,
isFile: stat.isFile()
};
results.push(item);
}
// 按文件大小降序排列
for (let i: number = 0; i < results.length - 1; i++) {
for (let j: number = i + 1; j < results.length; j++) {
if (results[j].fileSize > results[i].fileSize) {
let temp: FileStatResult = results[i];
results[i] = results[j];
results[j] = temp;
}
}
}
return results;
}
// 在页面中使用
async function startScan(filesDir: string): Promise<void> {
let task: taskpool.Task = new taskpool.Task(scanDirectory, filesDir);
let result: Object = await taskpool.execute(task, taskpool.Priority.HIGH);
let fileList: Array<FileStatResult> = result as Array<FileStatResult>;
for (let i: number = 0; i < fileList.length; i++) {
let item: FileStatResult = fileList[i];
console.info(`${item.fileName}: ${item.fileSize} bytes`);
}
}
七、最佳实践与常见陷阱
7.1 最佳实践
- 耗时操作放子线程:超过 16ms 的操作建议使用 TaskPool
- 合理使用优先级 :用户交互相关用
HIGH,后台任务用LOW - 大数据使用 ArrayBuffer :传输大量二进制数据时,使用
setTransferList避免复制开销 - 循环中检查取消状态 :长时间运行的任务应在循环迭代中检查
isCanceled() - 使用 for 循环替代高阶函数 :ArkTS 的
Array.from、map、filter等方法存在泛型推断限制,建议使用for循环 - 避免在子线程中访问 rawfile :rawfile 需要通过
resourceManagerAPI 访问,子线程不可用;将数据嵌入代码作为参数传入子线程
7.2 常见陷阱
| 陷阱 | 说明 | 正确做法 |
|---|---|---|
| 在 @Concurrent 中访问 UI | 子线程无法获取 getContext | 将数据通过参数传入,结果通过 Promise 返回 |
| 在 @Concurrent 中访问 rawfile | 子线程无法通过 fileIo 访问 rawfile 路径,抛出 No such file or directory | 将数据嵌入代码作为参数传入,或在主线程通过 resourceManager 读取后传入 |
| 传入类实例 | 类实例不可序列化 | 使用普通 interface 作为传输载体 |
| 忘记标记 @Concurrent | 运行时抛出 10200014 错误 | 所有在 TaskPool 中执行的函数必须标记 |
| 在任务中做无限期阻塞 | 占据工作线程,影响其他任务 | 设置超时或使用 isCanceled() 检查点 |
| 大量创建任务 | 内存压力过大 | 使用 TaskGroup 批量管理,控制并发量 |
7.3 @Concurrent 函数中的 import
@Concurrent 函数可以使用通过 import 导入的模块,例如 fileIo、util 等系统模块:
typescript
import { fileIo } from '@kit.CoreFileKit';
import { util } from '@kit.ArkTS';
@Concurrent
function readFileContent(filePath: string): string {
let fd: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
let stat: fileIo.Stat = fileIo.statSync(fd.fd);
let buf: ArrayBuffer = new ArrayBuffer(stat.size);
fileIo.readSync(fd.fd, buf);
fileIo.closeSync(fd.fd);
let decoder: util.TextDecoder = util.TextDecoder.create('utf-8');
return decoder.decodeWithStream(new Uint8Array(buf));
}
八、总结
@ohos.taskpool 是 HarmonyOS 中实现多线程的核心工具,其优势在于:
- 简单易用 :只需
@Concurrent+execute即可将任务放到子线程 - 自动管理:线程池自动扩缩容,无需手动管理生命周期
- 类型安全 :
GenericsTask提供编译期类型检查 - 灵活调度:支持优先级、延时执行、任务取消等高级特性
在实际开发中,建议遵循以下原则:
- 耗时操作交给 TaskPool,保持主线程流畅
- 使用普通
interface作为跨线程数据传输载体 - 在长任务中设置取消检查点
- 合理选择任务优先级,优化用户体验
系列文档:
📘
本文--- TaskPool API 详解与使用规范📗
小说文件查询案例指南--- 基础查询功能的完整实现过程📙
Navigation 页面导航实现指南--- 详情页跳转功能的添加步骤📕
小说查询案例总体介绍指南--- 案例总体效果与架构介绍📓
小说初始化实现指南--- 种子数据设计与批量文件初始化流程
参考文档: