你还在主线程里硬跑大循环?鸿蒙并发模型都摆这儿了,你真舍得让 UI 卡成 PPT 吗?
如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!✍️ 作者:某个被流“治愈”过的 移动端 老兵📅 日期:2025-11-05🧵 本文原创,转载请注明出处。
👋 你好,欢迎来到我的博客!我是【菜鸟学鸿蒙】
我是一名在路上的移动端开发者,正从传统“小码农”转向鸿蒙原生开发的进阶之旅。为了把学习过的知识沉淀下来,也为了和更多同路人互相启发,我决定把探索 HarmonyOS 的过程都记录在这里。
🛠️ 主要方向:ArkTS 语言基础、HarmonyOS 原生应用(Stage 模型、UIAbility/ServiceAbility)、分布式能力与软总线、元服务/卡片、应用签名与上架、性能与内存优化、项目实战,以及 Android → 鸿蒙的迁移踩坑与复盘。
🧭 内容节奏:从基础到实战——小示例拆解框架认知、专项优化手记、实战项目拆包、面试题思考与复盘,让每篇都有可落地的代码与方法论。
💡 我相信:写作是把知识内化的过程,分享是让生态更繁荣的方式。
如果你也想拥抱鸿蒙、热爱成长,欢迎关注我,一起交流进步!🚀
1. Worker 机制:不是“开个线程就完了”,而是“隔离执行环境 + 消息通信”🧠
Worker 的核心价值就一句话:
把耗时任务从 UI 主线程挪走,让 UI 线程专心渲染和交互。
它更像一个“独立小世界”:有自己的执行上下文,你和它沟通主要靠消息传递(postMessage/onmessage 那套)。你写 Worker 时最重要的心智是:不要指望共享变量能直接通用(多数时候不行),要么传消息,要么用共享内存(后面讲)。
1.1 Worker 的基本姿势:主线程创建 + 发消息 + 收结果 + 及时销毁
主线程(ArkTS/ETS)示例:
// Main.ets(示意)
import worker from '@ohos.worker'
@Entry
@Component
struct Page {
@State result: string = '—'
private w?: worker.ThreadWorker
aboutToAppear() {
// 1) 创建 Worker(路径按你的工程实际放)
this.w = new worker.ThreadWorker('workers/HeavyCalcWorker.ts')
// 2) 接收 Worker 消息
this.w.onmessage = (e) => {
this.result = `✅ 结果:${JSON.stringify(e.data)}`
}
this.w.onerror = (err) => {
this.result = `💥 Worker 崩了:${JSON.stringify(err)}`
}
}
doHeavy() {
// 3) 发消息:把参数传过去
this.w?.postMessage({ n: 2_000_000 })
this.result = '⏳ 计算中…别急别急'
}
aboutToDisappear() {
// 4) 及时关:别让 Worker 变成“后台老赖”
this.w?.terminate()
this.w = undefined
}
build() {
Column({ space: 12 }) {
Button('开始重计算').onClick(() => this.doHeavy())
Text(this.result)
}.padding(16)
}
}
Worker 线程(HeavyCalcWorker.ts)示例:
// HeavyCalcWorker.ts(示意)
import worker from '@ohos.worker'
const port = worker.workerPort
function heavy(n: number): number {
// 别笑,这就是最真实的“又臭又长”任务
let x = 0
for (let i = 0; i < n; i++) x = (x + i) % 1000003
return x
}
port.onmessage = (e) => {
const n = e.data?.n ?? 0
const ans = heavy(n)
port.postMessage({ ans, n })
}
现实里 Worker 最怕两件事:
1)创建太多(线程资源不是无限的)
2)不关(内存、句柄、定时器…你不关它就不走😅)
2. 线程通信方式:三条路——消息、可转移对象、共享内存(别乱选)📦
鸿蒙/ArkTS 的并发体系整体更偏 Actor 风格:消息通信为主,但也支持共享内存(SharedArrayBuffer)——只是你得自己处理同步互斥。
2.1 消息传递:最稳、最常用(但别传“巨型对象”)
- 优点:简单、安全、思维成本低
- 缺点:序列化/反序列化有开销,传大对象会肉疼
2.2 Transfer/Clone:大块二进制别“复制来复制去”
在 TaskPool 里,ArrayBuffer 默认是“转移”(转过去主线程就不能再用那份了),你也可以显式设置 transfer/clone 行为。
这点特别关键:你要是没搞懂“转移”,很容易出现“咦?我主线程的数据怎么变空了?”的灵异事件😵💫
2.3 SharedArrayBuffer + Atomics:真共享,但你得像写并发原语一样谨慎
共享内存的重点不在“能共享”,而在“怎么不打架”。官方论坛也强调:共享内存需要用原子操作或锁解决同步与互斥。
一个最小能跑的同步示意:
// shared.ts(示意:主线程创建共享内存交给并发任务)
const sab = new SharedArrayBuffer(4 * 2)
const a = new Int32Array(sab)
// a[0] = 数据槽,a[1] = 状态槽(0=未就绪,1=就绪)
a[0] = 0
a[1] = 0
并发侧写完数据后:
a[0] = 42
Atomics.store(a, 1, 1)
Atomics.notify(a, 1, 1)
主线程等待:
Atomics.wait(a, 1, 0) // 等待状态从0变1
console.info(`data=${a[0]}`)
共享内存适合高频、小块、需要避免拷贝的数据协作;
但如果你只是算个 JSON、做个图片压缩,优先消息/TaskPool,别把自己写进“并发地狱副本”。😅
3. 主线程负载控制:UI 线程要“轻”,别让它做苦力💢
华为 HarmonyOS 的性能最佳实践非常直白:
- 要避免主线程执行冗余与易耗时操作
- 高频回调场景里更要避免在回调里做耗时逻辑,否则会阻塞 UI 渲染导致卡顿
3.1 哪些算“主线程重活”?(你可能天天在做🙃)
- 大循环/复杂计算(加密、哈希、图像处理、排序)
- 大 JSON 解析/大对象深拷贝
- 高频事件回调里做 I/O 或复杂逻辑(滚动、触摸 move、动画 tick)
- 大量日志/trace(release 里也能把你拖慢)
3.2 一个很实用的“拆分公式”
主线程:采样 + 合并 + 派发
子线程/TaskPool:计算 + I/O + 批处理
举个你肯定遇到过的场景:列表搜索实时过滤。别在 onChange 每次输入就全量 filter 10 万条——那是自杀。
正确姿势(主线程节流 + 并发计算):
import { taskpool } from '@kit.ArkTS'
@Concurrent
function filterTask(all: string[], key: string): string[] {
const k = key.trim().toLowerCase()
if (!k) return all
return all.filter(x => x.toLowerCase().includes(k))
}
@Entry
@Component
struct SearchPage {
@State key: string = ''
@State list: string[] = []
private all: string[] = [] // 假设很大
private timer?: number
onKeyChange(v: string) {
this.key = v
// ✅ 主线程只做节流
if (this.timer) clearTimeout(this.timer)
this.timer = setTimeout(async () => {
// ✅ 重活丢给 TaskPool
const t = new taskpool.Task(filterTask, this.all, this.key)
this.list = await taskpool.execute(t)
}, 120) as unknown as number
}
build() {
Column() {
TextInput({ text: this.key, placeholder: '输入过滤' })
.onChange(v => this.onKeyChange(v))
// render this.list ...
}.padding(16)
}
}
TaskPool 的定位就是:应用提交任务,系统选合适的工作线程执行并返回结果;线程由系统统一管理并动态扩缩容。
另外它也有硬规则:并发任务函数要用@Concurrent标注;普通任务在工作线程里的同步执行时长不能超过 3 分钟,否则会被强制终止。
4. 并发最佳实践:别追“线程数”,追“吞吐 + 可控 + 可回收”✅
4.1 Worker vs TaskPool:怎么选不纠结?
- TaskPool:更适合“短平快任务、批处理、可并行的小函数”,你不想自己管理线程生命周期时就用它
- Worker:更适合“长生命周期、需要持续消息交互、流式任务/持续计算”的场景(比如持续解码、持续计算、后台管线)
一句狠话:
能 TaskPool 就别 Worker;必须常驻再 Worker。
(不然你很容易做出“十几个 Worker 常驻后台”的灾难工程😅)
4.2 通信别“碎片化轰炸”
- 频繁
postMessage会造成调度和序列化压力 - 把消息做成批次:比如 16ms/50ms 合并一次进度、日志、增量
4.3 数据传递:少传对象,多传“索引/句柄/ArrayBuffer”
- 大对象传来传去=你在跟 GC 玩命
- 能传
id就别传全量结构 - 二进制优先 ArrayBuffer(必要时用 transfer/clone 策略)
4.4 给并发任务加“止损机制”
- 超时取消(TaskPool 支持任务取消、优先级等能力)
- 可中断:把循环拆段,每段检查一次“是否已取消”
- 错误要回传主线程展示(别沉默失败,用户会以为你卡死)
4.5 永远记住:UI 更新只在 UI 线程做
你子线程算完了,回主线程再 set @State。这个规则别挑战——挑战了你也赢不了(只会赢来 bug 😅)。
结尾:你写的不是“多线程”,是“让用户感觉不到你在多线程”😄
并发模型写得好,用户只会觉得“哇好顺”;写得差,用户只会觉得“哇好卡”。
所以我最后反问你一句(带点坏笑):
你现在的并发,是在提升体验,还是在制造“偶现问题的快乐源泉”?😏
📝 写在最后
如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 👍、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!
我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!
感谢你的阅读,我们下篇文章再见~👋
✍️ 作者:某个被流“治愈”过的 移动端 老兵
📅 日期:2025-11-05
🧵 本文原创,转载请注明出处。
更多推荐




所有评论(0)