👋 你好,欢迎来到我的博客!我是【菜鸟学鸿蒙】
   我是一名在路上的移动端开发者,正从传统“小码农”转向鸿蒙原生开发的进阶之旅。为了把学习过的知识沉淀下来,也为了和更多同路人互相启发,我决定把探索 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
🧵 本文原创,转载请注明出处。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐