鸿蒙主线程为什么不能卡
鸿蒙 UI 渲染跑在主线程(也叫 JS 线程)上。主线程一旦被一段耗时计算堵住,界面就掉帧、卡顿,甚至 ANR 报错。所以"别在主线程干重活"是鸿蒙开发的第一原则。
重活挪出去,主线程只负责画界面。
这篇讲鸿蒙三套并发工具:TaskPool、Worker、协程,以及什么时候用哪个。
主线程(UI 线程)每秒要跑满六十帧,留给一帧的时间也就十几毫秒。任何一次网络等待、一段大循环、一次图片解码,只要超过这个数,这一帧就掉了,用户看到的就是滑动卡顿。所以凡是"能算完再回来"的活,都别占着主线程。养成"看见耗时函数先想它该跑在哪"的习惯,应用的流畅度会上一个台阶。
TaskPool:任务池
TaskPool 是鸿蒙推荐的轻量并发方案:把一个函数丢进任务池,它会在后台线程跑完再把结果交回来。适合一次性的重计算,比如数据解析、图片处理。
import taskpool from '@ohos.taskpool';
@Concurrent
function heavyCompute(n: number): number {
let sum = 0;
for (let i = 0; i < n; i++) sum += i;
return sum;
}
async runTask() {
const res = await taskpool.execute(heavyCompute, 1000000);
console.log('结果', res);
}
TaskPool 要点
- @Concurrent标记并发函数。
- 自动调度线程由池子管。
- 返回 Promiseawait 拿结果。
Worker:独立线程
Worker 是一条长期存在的后台线程,跟主线程通过消息通信。适合需要持续工作的场景,比如长连接、大文件读写。
// 主线程
const worker = new worker.ThreadWorker('entry/ets/workers/my.ets');
worker.postMessage({ type: 'parse', data: raw });
worker.onmessage = (e) => { console.log('完成', e.data) };
Worker 是双向通信,来回发消息;TaskPool 是一次性任务池。
协程与异步
ArkTS 的 async/await 是单线程异步:I/O 等待时主线程不阻塞,等数据回来再继续。它不真正并行,但能让网络请求、读文件这类等待不卡界面。
三者怎么选
- 一次性重计算 → TaskPool。
- 长期后台线程、消息往来 → Worker。
- I/O 等待、不真并行 → async/await。
实战:挪一个重计算
把一段在主线程里跑会卡界面的数据解析,改成 TaskPool 执行,界面立刻顺了。
// 改造前:主线程解析会卡
// 改造后:
@Concurrent
function parseLarge(list: string[]): object[] {
return list.map(JSON.parse);
}
onPageShow() { taskpool.execute(parseLarge, raw).then(this.bindData) }
小结
鸿蒙并发三板斧:TaskPool 做一次性重算、Worker 做长期后台线程、async/await 做 I/O 等待。记住主线程只画界面,重活一律挪出去,应用自然流畅。