ArkTS 的并发安全处理是其“高性能”与“高稳定性”平衡点的体现。它通过内存隔离从物理上杜绝了传统多线程中的“抢占”问题,但在应用开发中也引入了更严谨的通信规则。
1. ArkTS 如何处理并发安全?
与 Java 或 C++ 不同,ArkTS 默认采用 Actor 模型(内存隔离) 。
- 隔离机制: 每个线程(UI 线程、TaskPool 任务、Worker)都拥有独立的运行实例(引擎实例)。线程之间不共享内存,这意味着你无法在线程 A 中直接修改线程 B 的变量。
- 无锁化设计: 因为没有共享内存,所以不需要互斥锁(Mutex)来防止脏读,这彻底消除了死锁(Deadlock)风险。
- @Sendable 装饰器: 如果你确实需要在线程间传递复杂对象,ArkTS 引入了
@Sendable。被装饰的类在跨线程传递时,系统会确保其引用计数和内存访问是线程安全的。
2. 是否允许跨线程更新 UI?
结论:严禁跨线程直接操作 UI。
这是所有主流现代操作系统的共同选择。
- 原因: UI 组件(如
Text,Button)是非线程安全的。如果允许后台线程直接修改 UI,可能会导致渲染引擎在绘制时发生严重的内存冲突或布局混乱。 - 限制: 你不能在
TaskPool或Worker中调用任何 ArkUI 的组件接口。只有 UI 主线程 才有权操作视图树。
3. 后台线程如何通知 UI?
既然不能直接更新,后台线程需要通过“异步消息”将结果发回主线程,由主线程代为执行更新逻辑。
方式 A:TaskPool 的自动返回 (推荐)
taskpool.execute() 会返回一个 Promise。当后台任务执行完毕,结果会自动“漂流”回主线程的 .then() 回调中。
TypeScript
// 主线程
taskpool.execute(myTask).then((result) => {
this.uiData = result; // 在主线程安全更新 UI
})
方式 B:TaskPool.sendData 实时通知
如果你需要在后台任务进行中(如进度条更新)不断通知 UI,可以使用 sendData。
- 后台: 任务内部调用
taskpool.Task.sendData(progress)。 - 主线程: 通过
task.onReceiveData接收消息并更新状态。
方式 C:Emitter 事件总线
对于更解耦的场景(如后台下载服务通知任意页面更新),可以使用 Emitter 发送全局事件。
总结
| 并发特性 | 传统多线程 (Java/C++) | ArkTS (HarmonyOS) |
|---|---|---|
| 内存访问 | 共享内存 (Shared Memory) | 内存隔离 (Isolated) |
| 同步工具 | 锁、信号量 (Mutex/Lock) | 消息传递 (Message Passing) |
| UI 更新 | 需手动切回 UI 线程 | 强制切回 UI 线程 |
| 数据安全 | 容易出现竞态条件 | 编译期/运行时强制安全 (@Sendable) |
一句话总结: 在 ArkTS 中,后台线程就像是独立工作的办公室,它们通过“发邮件(消息)”告知 UI 主线程任务进度,而主线程则是唯一有权在“黑板(屏幕)”上写画的人。