HarmonyOS ArkTS 并发编程:TaskPool 多线程实战
适用版本:HarmonyOS 6.1(API 12)及以上
验证环境:Pura 90 Pro 模拟器(HarmonyOS 6.1.1,API 24)
关键 API:@kit.ArkTS→taskpool
前言
移动应用中最常见的卡顿原因之一,就是在主线程里跑了耗时运算——图片解码、数据加密、大量数学计算……一旦主线程被占满,UI 渲染帧就会丢失,用户就会感受到明显卡顿。
HarmonyOS 提供了 TaskPool(任务池) 来解决这个问题:把计算密集型任务扔进线程池,主线程继续响应用户操作,任务完成后通过 await 自然地拿回结果更新 UI。本文用三个并发计算任务演示完整用法。
核心概念
TaskPool 工作原理
主线程(UI) 子线程池(TaskPool)
│ │
│── taskpool.execute(task) ──▶ │ 线程 1: computeFib(40)
│── taskpool.execute(task) ──▶ │ 线程 2: computeFib(45)
│── taskpool.execute(task) ──▶ │ 线程 3: computePrimes(500000)
│ │
│◀── await 结果 ─────────────── │ 三个任务并发,各自独立
│ 更新 UI(@State 驱动刷新) │
三个关键规则:
@Concurrent装饰器:任务函数必须是顶层函数,且加@Concurrent标注,编译器会验证其可序列化性- 数据传递靠序列化:参数和返回值在跨线程传输时会被序列化,不能传递复杂对象(如 UI 组件)
await在主线程恢复:taskpool.execute()返回 Promise,await之后的代码自动回到主线程,可安全更新@State
完整代码
任务函数(顶层 + @Concurrent)
import { taskpool } from '@kit.ArkTS'
// ✅ 必须是顶层函数,不能是类方法或 lambda
@Concurrent
function computeFibonacci(n: number): number {
if (n <= 1) return n
let a = 0
let b = 1
for (let i = 2; i <= n; i++) {
const c = a + b
a = b
b = c
}
return b
}
@Concurrent
function computePrimes(limit: number): number {
// 线性筛,返回质数个数
const sieve: boolean[] = new Array<boolean>(limit + 1).fill(true)
sieve[0] = false
if (limit >= 1) { sieve[1] = false }
for (let i = 2; i * i <= limit; i++) {
if (sieve[i]) {
for (let j = i * i; j <= limit; j += i) {
sieve[j] = false
}
}
}
let count = 0
for (let k = 0; k <= limit; k++) {
if (sieve[k]) { count++ }
}
return count
}
状态类型定义
// ArkTS 严格模式下不支持 interface 字面量初始化,用 class
type TaskStatus = 'idle' | 'running' | 'done' | 'error'
class TaskResult {
label: string = ''
result: string = '-'
duration: number = 0
status: TaskStatus = 'idle'
}
页面组件(核心逻辑)
@Entry
@Component
struct TaskPoolDemo {
@State tasks: TaskResult[] = TaskPoolDemo.initTasks()
@State runningCount: number = 0
@State totalTime: number = 0
static initTasks(): TaskResult[] {
const t0 = new TaskResult()
t0.label = 'Fibonacci(40)'
const t1 = new TaskResult()
t1.label = 'Fibonacci(45)'
const t2 = new TaskResult()
t2.label = 'Primes < 500000'
return [t0, t1, t2]
}
// 不可变更新:每次生成新数组触发 @State 刷新
private setTask(index: number, result: string, duration: number, status: TaskStatus): void {
const updated: TaskResult[] = []
for (let i = 0; i < this.tasks.length; i++) {
const t = new TaskResult()
t.label = this.tasks[i].label
if (i === index) {
t.result = result
t.duration = duration
t.status = status
} else {
t.result = this.tasks[i].result
t.duration = this.tasks[i].duration
t.status = this.tasks[i].status
}
updated.push(t)
}
this.tasks = updated
}
private async runFibTask(index: number, n: number): Promise<void> {
this.setTask(index, '计算中…', 0, 'running')
const start = Date.now()
try {
const task = new taskpool.Task(computeFibonacci, n)
const result = await taskpool.execute(task) as number
// await 之后自动在主线程,可直接更新 @State
this.setTask(index, result.toString(), Date.now() - start, 'done')
} catch (e) {
const err = e as Error
this.setTask(index, `错误: ${err.message}`, 0, 'error')
}
}
private async runPrimeTask(index: number, limit: number): Promise<void> {
this.setTask(index, '计算中…', 0, 'running')
const start = Date.now()
try {
const task = new taskpool.Task(computePrimes, limit)
const count = await taskpool.execute(task) as number
this.setTask(index, `共 ${count} 个质数`, Date.now() - start, 'done')
} catch (e) {
const err = e as Error
this.setTask(index, `错误: ${err.message}`, 0, 'error')
}
}
private async runAllTasks(): Promise<void> {
const globalStart = Date.now()
this.runningCount = 3
// Promise.all 让三个任务真正并发,而不是串行等待
await Promise.all([
this.runFibTask(0, 40),
this.runFibTask(1, 45),
this.runPrimeTask(2, 500000),
])
this.totalTime = Date.now() - globalStart
this.runningCount = 0
}
// ... build() 见完整源码
}
完整源码:
entry/src/main/ets/pages/Index.ets(见配套仓库)
模拟器运行效果
初始状态
三个任务均处于「等待」状态,绿色按钮可点击。

执行完成状态
点击「并发执行全部」后,三个任务在线程池中并发执行,主线程 UI 不阻塞。

实测结果:
| 任务 | 结果 | 耗时 |
|---|---|---|
| Fibonacci(40) | 102,334,155 | 46ms |
| Fibonacci(45) | 1,134,903,170 | 44ms |
| Primes < 500000 | 41,538 个质数 | 69ms |
| 总并发耗时 | — | 76ms |
三个任务各自独立,总耗时约等于最慢单任务(69ms),而非三者之和(159ms),验证了真正的并发执行。
关键点总结
1. @Concurrent 是强制要求
不加装饰器会在编译期报错。这是编译器的静态保证,确保函数内没有引用任何不可序列化的外部状态(如 UI 组件、闭包捕获变量)。
2. ArkTS 不支持 { ...spread } 更新对象
ArkTS 严格模式(arkts-no-spread)禁止对象扩展运算符。更新 @State 数组时需要手动遍历创建新数组,这也符合不可变数据原则。
3. Promise.all 真并发 vs 串行 await
// ❌ 串行:总耗时 = 任务1 + 任务2 + 任务3
await task1(); await task2(); await task3();
// ✅ 并发:总耗时 ≈ max(任务1, 任务2, 任务3)
await Promise.all([task1(), task2(), task3()]);
4. 错误处理不能省略
TaskPool 任务失败会以 rejected Promise 的形式返回,必须用 try/catch 捕获,否则未处理的 rejection 会导致应用崩溃。
常见问题
Q: 为什么不能用类方法作为 TaskPool 任务函数?
A: 类方法依赖 this,而 this 可能引用不可序列化的 UI 状态。编译器强制要求顶层函数,从根源上规避这类问题。
Q: TaskPool 和 Worker 如何选择?
A: 优先用 TaskPool——系统自动管理线程数量和生命周期,适合一次性计算任务。Worker 适合需要长驻后台、持续双向通信的场景(如 WebSocket 处理、音频流处理)。
Q: 可以向 TaskPool 任务传递数组吗?
A: 可以,数组会被序列化传递。但如果数组很大(MB 级),建议改用 SharedArrayBuffer + @Sendable 对象实现零拷贝传递,详见第 3 篇:Sendable 对象模型。
更多推荐

所有评论(0)