一、背景

在鸿蒙开发中提供了两种多线程并发方案,分别是TaskPool与Worker,此篇文章主要总结下TaskPool

二、TaskPool概念

1、TaskPool是为应用提供多线程运行环境,旨在降低资源消耗并提升系统性能

2、开发者无需关心线程的生命周期,由系统统一管理线程的创建、调度和销毁

3、目标:简化「轻量、短期、独立任务」的异步执行,解决开发者手动管理线程的繁琐性和资源浪费问题

三、注意事项

1、装饰器:仅支持在.ets文件中使用

A、实现任务的函数需要使用@Concurrent装饰器

B、从API version 11开始,跨并发实例传递带方法的实例对象时,该类必须使用装饰器@Sendable装饰器

2、耗时要求

A、在TaskPool工作线程中的执行时长不能超过3分钟(LongTask除外)

B、可通过Task的属性ioDuration、cpuDuration获取执行当前任务的异步IO耗时和CPU耗时

3、任务函数的入参类型

A、入参需满足序列化支持的类型

B、目前不支持使用@State装饰器、@Prop装饰器、@Link装饰器等装饰器修饰的复杂类型

4、ArrayBuffer参数

A、默认转移,需要设置转移列表可通过接口setTransferList()设置

B、若要多次使用ArrayBuffer,可通过setCloneList()改为拷贝传递

5、模块使用限制

A、只能使用线程安全的模块,不能使用UI相关的非线程安全模块,只支持在主线程中使用的模块(ApplicationContext)

B、不支持在线程中使用AppStorage

6、其他注意项

A、序列化传输的数据量限制为16MB

B、Promise不支持跨线程传递

C、不支持指定任务所运行的线程,任务会被分配到空闲的线程中执行

D、IDLE 优先级任务仅在所有线程空闲时执行,且同一时间只执行一个

四、怎么用

使用TaskPool流程:定义任务 → 提交任务 → 处理结果 → 取消任务(可选)

步骤一:定义耗时任务(必须用 @Concurrent 标记)

所有提交到 TaskPool 的耗时函数,必须通过 @Concurrent 装饰器标记——这是鸿蒙的强制要求,用于告知系统“该函数是耗时任务,需在后台线程执行”。

注意:该函数需满足「无状态、参数可序列化」,不能直接操作 UI(如修改 @State 变量、调用 UI 组件方法)。

@Concurrent
function computeTask(a: number, b: number): number {
  let start = Date.now();
  while (Date.now() - start < 2000) {
  } // 阻塞2秒(后台线程,不影响UI)
  return a + b;
}

步骤二、提交任务+处理结果

创建 Task 实例,将函数和参数包装起来;提交到任务池执行,并等待 Promise 返回的结果

export class taskpoolUtils {
  private static instance: taskpoolUtils | undefined;

  public static getInstance(): taskpoolUtils {
    if (!taskpoolUtils.instance) {
      taskpoolUtils.instance = new taskpoolUtils();
    }
    return taskpoolUtils.instance;
  }

  async runConcurrentTask() {
    try {
      // 1. 同步创建Task实例
      const task: taskpool.Task = new taskpool.Task(computeTask, 10, 20);
      // 2. 提交任务到后台线程
      const result = await taskpool.execute(task);
      console.log('lucy== 计算结果result', result); // 2秒后打印30
    } catch (err) {
      // 捕获任务执行异常
      console.error('lucy== 任务执行失败', JSON.stringify(err));
    }
  }
}

最终效果执行

import { ScreenUtils } from '../utils/ScreenUtils';
import { taskpoolUtils } from '../utils/taskpoolUtils'

@Entry
@Component
struct Index {
  private globalNavStack: NavPathStack = new NavPathStack();
  //UI状态,验证taskpool不阻塞UI
  @State uiText: string = '未执行任务';

  build() {
    Column() {
      Navigation(this.globalNavStack) {
        // 显示UI状态,验证是否阻塞
        Text(this.uiText).fontSize(20).margin(20);

        Button('跳转到login组件+执行耗时任务')
          .onClick(() => {
            // 1. 先更新UI(验证UI线程没被阻塞)
            this.uiText = '任务执行中...';
            // 2. 跳转页面(UI操作)
            this.globalNavStack.pushPathByName('loginPage', null, false);
            // 3. 执行taskpool耗时任务
            taskpoolUtils.getInstance().runConcurrentTask().then(() => {
              // 任务完成后更新UI
              this.uiText = '任务执行完成';
            });

            // 4. 验证:任务执行中,UI仍能响应(立即打印,不会等2秒)
            console.log('lucy== UI线程未阻塞,立即执行');
          })
      }
      .height('100%')
      .width('100%')
      .padding({ top: ScreenUtils.getInstance().getStatusBarHeight() })
    }
  }
}

五、实战场景

使用 TaskPool 流程:定义任务 → 提交任务 → 处理结果 → 取消任务(可选)

5.1、项目背景

项目中使用taskpool实现插件下载的功能,结合实际项目代码来梳理下 TaskPool 流程

5.2、具体步骤

步骤1:定义耗时任务

必须用 @Concurrent 标记,支持 @Sendable 传递复杂对象

补充:@Sendable 标记复杂传递对象

步骤2:提交任务 + 处理结果

创建 Task 实例包装耗时函数与参数,提交到 TaskPool 执行,并通过 then/catch 处理结果 / 异常;支持配置任务优先级,还可通过 onReceiveData 接收后台任务的中间通信数据。

六、适用场景

A、短耗时任务:执行时间建议 ≤ 5 秒(如缓存检查、MD5 校验、小型数据计算、接口参数加密);

B、无状态任务:任务执行不依赖外部状态,多次执行结果一致(如相同参数的 MD5 计算,结果始终相同);

C、高并发任务:需要同时执行多个独立任务(如批量下载多个小插件的前置检查);

D、优先级敏感任务:不同任务有优先级差异(如用户主动触发的下载 > 后台自动更新)。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐