HarmonyOS7 TaskPool 多线程:耗时计算不卡 UI

📅 2026/7/14 18:03:01 👁️ 阅读次数 📝 编程学习
HarmonyOS7 TaskPool 多线程:耗时计算不卡 UI

文章目录

    • 前言
    • 为什么不能主线程算
    • TaskPool 基本用法
      • 代码实现
    • 派发任务并拿结果
    • 取消任务 & 任务组
      • 核心代码
    • TaskPool vs Worker 怎么选
    • 常见误区(小白必踩)
    • 完整可运行示例:TaskPool 算质数
      • 完整示例
    • 写在最后

前言

你在主线程算个加密、解个压、处理几万条数据,界面就"冻"住了——点不动、滑不了,用户以为 app 死了。
HarmonyOS 提供了TaskPool(任务池)把这种重活丢到后台线程,主线程专心管 UI。这篇文章讲清怎么用 TaskPool 跑耗时任务,还顺带提一下和Worker的区别。

为什么不能主线程算

主线程(UI 线程)负责响应用户操作、刷新界面。你塞个 3 秒的循环进去,这 3 秒内它啥也干不了——界面就卡死。

解法:把重活扔到后台线程,算完了把结果交回主线程。

TaskPool 基本用法

TaskPool 适合"跑一次、短任务、要结果"的场景。先定义一个能被序列化的任务函数

代码实现

// 任务函数:必须是顶层函数,参数和返回值要可序列化functionheavyCalc(taskParam:number):number{letsum=0for(leti=0;i<taskParam;i++){sum+=Math.sqrt(i)*Math.sin(i)// 假装很耗时}returnsum}

要点:

  • 任务函数要放在模块顶层(不能在组件类里)。
  • 参数和返回值必须是可序列化类型(number/string/对象等),不能传函数或闭包。

派发任务并拿结果

import{taskpool}from'@kit.ExecutionKit'asyncfunctionrunTask(){consttask=newtaskpool.Task(heavyCalc,1000000)// 包装成任务constresult=awaittaskpool.execute(task)// 派发执行console.info('计算结果:'+result)// 主线程拿到结果}
  • new taskpool.Task(fn, arg):把函数和参数包成任务。
  • taskpool.execute(task):丢进线程池,返回 Promise,算完拿结果。
  • 整个过程主线程不阻塞,UI 照常响应。

execute是 Promise,用await等结果,或者用.then()。界面不会因为后台在算而卡住。

取消任务 & 任务组

长时间任务可以取消,多个任务可以分组:

核心代码

// 取消consttask=newtaskpool.Task(heavyCalc,9999999)constpromise=taskpool.execute(task)taskpool.cancel(task)// 不需要了就取消// 任务组:等所有完成constgroup=newtaskpool.TaskGroup()group.addTask(newtaskpool.Task(heavyCalc,1000))group.addTask(newtaskpool.Task(heavyCalc,2000))awaittaskpool.execute(group)

TaskPool vs Worker 怎么选

维度TaskPoolWorker
适用短任务、跑完即弃长驻、常通信
通信只传最终结果可双向持续通信
创建成本低(池化复用)高(每个独立线程)
典型场景单次计算、加解密持续监听、长连接处理

经验:单次重计算用 TaskPool;需要长期驻扎、反复通信的后台逻辑(如音视频处理线程)用 Worker。

常见误区(小白必踩)

误区说明
任务函数写在组件类里TaskPool 任务必须是模块顶层函数,写在类里序列化失败。
参数不可序列化传函数、闭包会报错,参数返回值用 number/string/普通对象。
重活在主线程硬扛把循环、解析丢进 TaskPool,主线程才不卡。

下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。

完整可运行示例:TaskPool 算质数

把耗时计算丢到线程池,UI 不卡。

完整示例

// TaskPool 执行耗时任务,结果回主线程import{taskpool}from'@kit.ArkTS'@ConcurrentfunctioncountPrimes(max:number):number{letcnt=0for(leti=2;i<max;i++){letp=truefor(letj=2;j*j<=i;j++){if(i%j===0){p=false;break}}if(p)cnt++}returncnt}@Entry@Componentstruct Calc{@Stateresult:string='点按钮开始计算'build(){Column({space:16}){Text(this.result).fontSize(18)Button('算 1~200000 的质数').onClick(async()=>{consttask=newtaskpool.Task(countPrimes,200000)constn=awaittaskpool.execute(task)// 不阻塞 UIthis.result='共有 '+n+' 个质数'})}.padding(20)}}

你会看到什么:点按钮后,即使计算要几百毫秒,界面依然能拖动、点击不卡顿;算完结果自动回到主线程更新文字。

动手改一改

  • 200000改成1000000,对比 UI 是否有感卡顿(应该没有)。
  • taskpool.execute(task, taskpool.Priority.HIGH)提优先级。
  • @Concurrent函数拆成多段,用taskpool并行多个任务。

写在最后

TaskPool 是"把重活丢后台"的首选:定义顶层任务函数 →new Task包装 →execute派发 →await拿结果。主线程全程不卡,用户体验直接起飞。
下次你写循环、解析、计算前先问一句:"这活重吗?"重的话,丢进 TaskPool,别在主线程硬扛。