Appearance
UI 线程与工作线程
理解主线程职责、子线程限制,以及如何安全地在 UI 和工作线程间传递数据。
1. UI 线程(主线程)职责
1.1 主线程职责
UI 线程负责:
├─ UI 渲染(刷新屏幕)
├─ 事件响应(点击、触摸、滚动)
├─ 生命周期回调
├─ 状态更新(@State 变化触发)
└─ 动画执行1.2 主线程卡顿原因
❌ 绝对不能在主线程做:
├─ 网络请求(耗时几百毫秒)
├─ 文件 I/O(读/写文件)
├─ 大量 JSON 解析
├─ 复杂计算(加密、图像处理)
├─ 数据库查询(大量数据)
├─ 图片加载/解码
└─ 同步睡眠(Thread.sleep)
后果:
├─ 卡顿(>16ms 掉帧)
├─ ANR(Application Not Responding)
└─ 用户体验极差1.3 卡顿检测
typescript
// 主线程耗时检测
let start = Date.now()
// 耗时操作(错误示范)
heavyOperation()
let duration = Date.now() - start
if (duration > 100) {
console.warn('主线程耗时过长:', duration + 'ms')
}
// 正确做法:用 TaskPool
TaskPool.execute(() => heavyOperation())
.then(() => {
// 结果处理(在子线程)
context.getMainContext().syncDispatch(() => {
this.data = result // 回到主线程更新 UI
})
})2. 工作线程
2.1 工作线程分类
| 线程类型 | 适用场景 | 管理方式 |
|---|---|---|
| TaskPool 线程 | 短时任务(< 500ms) | 系统自动 |
| Worker 线程 | 长时任务(> 500ms) | 手动管理 |
| 原生线程 | 特殊场景 | 手动管理 |
2.2 工作线程与 UI 线程通信
typescript
// 工作线程处理
TaskPool.execute(() => {
// 1. 处理数据
let data = loadData()
// 2. 回到主线程更新 UI
context.getMainContext().syncDispatch(() => {
// 修改 @State
this.data = data
this.isLoading = false
})
return data
}).then((result) => {
// TaskPool 的 then 也在主线程执行
console.log('结果:', result)
})3. @State 线程安全
3.1 @State 只能在 UI 线程修改
typescript
@Component
struct MyComponent {
@State count: number = 0
// ❌ 错误:子线程修改 @State
wrongMethod() {
TaskPool.execute(() => {
this.count++ // ❌ 运行时错误
})
}
// ✅ 正确:子线程处理,UI 线程更新
correctMethod() {
TaskPool.execute(() => {
let result = heavyComputation()
context.getMainContext().syncDispatch(() => {
this.count = result // ✅ 在主线程更新
})
})
}
}3.2 线程安全的状态传递
typescript
// 工作线程 → UI 线程(通过序列化传递)
TaskPool.execute((data: WorkerData): SerializedData => {
return serialize(data) // 自动序列化
}).then((result: SerializedData) => {
// 回到主线程,结果已反序列化
this.data = result
})4. 线程模型对比
4.1 Android vs HarmonyOS
Android 线程模型 HarmonyOS 线程模型
┌─────────────┐ ┌──────────────┐
│ Main Thread │ │ UI 线程 │
│ (Looper) │ │ │
├─────────────┤ ├──────────────┤
│ Worker │ 共享内存 │ Worker │ 消息传递
│ Thread │──↕── 同步 │ Thread │ 序列化
├─────────────┤ ├──────────────┤
│ TaskThread │ │ TaskPool │ 自动管理
│ (Executor) │ │ Thread │
└─────────────┘ └──────────────┘
核心差异:
├─ Android:共享内存 + 同步
└─ 鸿蒙:内存隔离 + 消息传递5. 性能优化
5.1 减少线程切换
❌ 频繁切换:
├─ 子线程 → 主线程 → 子线程 → 主线程(来回切换)
└─ 开销大,性能差
✅ 合并处理:
├─ 子线程处理完所有数据
└─ 一次回到主线程更新 UI5.2 线程池复用
typescript
// ❌ 错误:每次创建新线程
for (let i = 0; i < 100; i++) {
TaskPool.execute(() => { ... })
}
// ✅ 正确:复用已有线程(TaskPool 自动实现)
TaskPool.execute(() => { ... })
TaskPool.execute(() => { ... })
// TaskPool 自动复用线程6. 面试高频考点
Q1: 主线程卡死的常见原因?
回答:在 aboutToAppear 或点击事件中进行同步 I/O 操作、复杂 JSON 解析或大量计算。解决方法:用 TaskPool 放后台、async/await。
Q2: @State 能在子线程修改吗?
回答:不可以。@State 必须在 UI 线程修改。子线程处理完后通过 context.getMainContext().syncDispatch() 回到主线程更新。
🐱 小猫提示:线程模型记住 "主线程管 UI、子线程做事、通过 syncDispatch 回主线程更新 @State"。