[鸿蒙从零到一] ArkTS Sendable 数据模型与跨线程共享实战

简介: Sendable 是 ArkTS 实现跨线程零拷贝共享的核心机制:通过编译期类型校验与 Shared Heap 运行时支持,使对象真正被多线程共用。需显式标注、字段限于基础类型或 Sendable 容器(如 collections.Map),并发写操作须配合 AsyncLock 保障一致性。适用于大数据只读或可变状态共享场景,小数据仍推荐结构化克隆。

为什么需要 Sendable

ArkTS 的多线程模型(taskpool、Worker)不像 JVM 那样共享堆内存。跨线程默认走结构化克隆(structured clone),代价是拷贝开销 + 引用断裂:主线程改了一份,子线程看不到。业务里但凡涉及大列表、缓存、状态机在多线程之间流动,克隆语义就成了性能与一致性的双重瓶颈。
Sendable 数据模型的定位就是让一份对象真正跨线程共享,而不是每次传递都深拷贝。它是 ArkTS 静态类型系统 + 运行时并发原语共同支撑出来的机制,本质上把"可跨线程"变成了一种编译期能校验的对象契约。

原理:Sendable 到底做了什么

Sendable 对象在运行时被分配到一个跨线程可访问的堆区域(Shared Heap)。它的字段类型受限,只能是:

  • 基础类型(number / string / boolean / bigint / null / undefined)
  • 其他 Sendable 类实例
  • Sendable 容器:collections.Arraycollections.Mapcollections.Set

关键约束有三条:

  • 字段必须显式声明类型,不允许 any / unknown / 联合含非 Sendable 类型。
  • 方法内不能捕获非 Sendable 的外部变量(闭包被静态检查器拒绝)。
  • 并发访问不是自动加锁,共享 = 可见性;一致性需要开发者自己用 ArkTSUtils.locks.AsyncLock 或 CAS 语义保证。

这三条决定了 Sendable 是"零拷贝但有代价"的模型:省下克隆开销,换来对并发正确性的显式管理。

最小可运行示例:跨线程共享一个计数器

`// shared_counter.ets
import { taskpool } from '@kit.ArkTS';
import { collections } from '@kit.ArkTS';
import { ArkTSUtils } from '@kit.ArkTS';

@Sendable
export class SharedCounter {
private value: number = 0;
private lock: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock();

async increment(): Promise<number> {
return this.lock.lockAsync(() => {
this.value += 1;
return this.value;
});
}

get(): number {
return this.value;
}
}

@Concurrent
async function bump(counter: SharedCounter, times: number): Promise<number> {
let last = 0;
for (let i = 0; i < times; i++) {
last = await counter.increment();
}
return last;
}

export async function runDemo(): Promise<number> {
const counter = new SharedCounter();
const tasks: Promise<number>[] = [];
for (let i = 0; i < 4; i++) {
tasks.push(taskpool.execute(bump, counter, 1000) as Promise<number>);
}
await Promise.all(tasks);
return counter.get(); // 期望 4000
}
这里的重点:counter只 new 了一次,四个 taskpool 任务拿到的是同一个实例的引用。如果去掉@Sendable,运行时会报"参数不可跨线程";如果去掉AsyncLock,四个 worker 竞争value += 1` 会拿到少于 4000 的结果——共享内存意味着竞态可复现,这一点比 structured clone 时代更需要谨慎。

Sendable 容器 vs 普通数组

常规 Array&lt;T&gt; 不是 Sendable,扔进 taskpool 只能触发克隆。真正做到共享要用 @kit.ArkTScollections 命名空间:
`import { collections } from '@kit.ArkTS';

@Sendable
export class HotCache {
data: collections.Map<string, string> = new collections.Map();
put(k: string, v: string): void { this.data.set(k, v); }
get(k: string): string | undefined { return this.data.get(k); }
}
差异在于collections.Map底层实现了跨线程内存屏障与迭代安全;Map/ 普通Array只在当前 VM 隔离区可见。混用最常见的坑是:把普通Array塞到@Sendable` 类的字段里,编译器直接拒绝;而在方法内部临时构造普通数组再返回,则会在返回值路径上触发克隆,抵消了共享的意义。

性能对比:克隆 vs 共享

用一个 10 万条记录的列表做基准(DevEco Profiler 采样,Mate 60 Pro,冷启动后稳定态平均值):

方案主线程传递耗时子线程读耗时峰值内存

普通 Array<Record> + taskpool~180ms(结构化克隆)~4ms2× 数据大小
@Sendable + collections.Array<1ms(引用传递)~5ms(首次跨线程访问轻微开销)1× 数据大小

结论清晰:数据越大,Sendable 的收益越接近线性放大;小对象反而看不出差别,此时用 structured clone 更简单。选型的分界线一般在"单次传递 > 1 万条 / 1MB"这个量级。

常见踩坑清单

  • 忘记标 @Sendable:类字段互相引用时,只要有一个漏标,编译器就会把整棵引用图判为不可跨线程。
  • 字段用了普通 Array/Map:换成 collections.*,否则运行时抛 BusinessError 10200201
  • 方法内捕获 UI 状态@StateAppStorage 都不是 Sendable,禁止在 Sendable 类的方法里直接访问;应在主线程侧读出快照再传参。
  • 忽略锁:Sendable = 共享内存,不 = 自动同步;写操作必须走 AsyncLock 或原子容器。
  • 把 Sendable 对象持久化:Preferences / relationalStore 不识别 Sendable 元数据,落盘前要转成 POJO 或 JSON。

方法论:什么时候该用 Sendable

把跨线程数据流分成三类,选型就清晰了:

  • 一次性任务参数(< 100KB,用完即抛):直接 structured clone,代码最简单。
  • 大批量只读数据(列表、字典、模型输入):Sendable + collections,省克隆开销。
  • 可变共享状态(缓存、计数器、状态机):Sendable + AsyncLock,把并发正确性显式化到代码里。

不要把 Sendable 当成"高性能默认选项"。它引入的心智负担是真实的:一旦对象跨线程共享,任何写操作都要重新审视竞态。工程上更好的做法是——先用 structured clone 跑通业务,等 Profiler 指认克隆成为瓶颈时再迁移到 Sendable

小结

Sendable 是 ArkTS 并发模型从"消息传递"走向"共享内存"的关键一步。它借助严格类型系统把可共享性做成了编译期契约,用 collections 容器把跨线程内存屏障做成了运行时能力,用 AsyncLock 把同步义务显式化。理解它的三条约束、掌握它的性能边界、遵守它的锁纪律,才能让多线程代码在鸿蒙上真正跑出该有的速度。

相关文章
|
17天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
12764 75
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
5天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
10天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1618 2
|
人工智能 JavaScript 开发工具
DeepSeek Harness 本地安装与使用指南
DeepSeek Harness(DSH)是DeepSeek AI开源的Agent运行框架,支持本地文件操作、命令执行与工具调用。基于Cordis插件架构,具备高扩展性与强可控性,适合开发者搭建可控Agent环境或开展模型基准测试。当前为开发者预览版,需Node.js环境,推荐先用`npx @deepseek-ai/dsh web`快速体验。
4989 0
|
11天前
|
人工智能 Java BI
【AI】DeepSeek Harness 安装、运行、管理插件
本文介绍了如何运行DeepSeek开源的Agent框架DeepSeek Harness(dsh)。主要内容包括:使用nvm安装适配的Node版本;通过代理加速克隆GitHub源码;使用pnpm安装依赖并启动项目;配置DeepSeek API Token;安装扩展功能的插件。该框架自带Web界面,支持模型适配、文件编辑等插件化功能
1740 1
|
13天前
|
人工智能 JavaScript 测试技术
保姆级教程:DeepSeek Harness从安装到跑通测试,30分钟上手
DeepSeek Harness是DeepSeek开源的AI Agent运行时,主打“一行命令安装、5分钟跑通”。它让模型真正动手干活——读代码、跑测试、分析失败、生成修复方案。本文手把手教你30分钟从零上手,覆盖安装、配置、实测及避坑指南,助你快速掌握下一代AI编程范式。
|
15天前
|
开发工具 Swift git
DeepSeek Harness 插件推荐:4 款开源神器让写代码直接起飞
DeepSeek Harness 插件推荐:ModLens 视觉、Web UI 全家桶、Mac 原生与 GenUI 渲染,4 款开源插件给纯文本模型补齐短板。
2022 6
DeepSeek Harness 插件推荐:4 款开源神器让写代码直接起飞
|
12天前
|
人工智能 JavaScript 测试技术
从 0 到 1,DeepSeek Harness 保姆级安装与使用教程!
DeepSeek Harness是DeepSeek推出的开源Agent运行框架,秉持“一切皆插件”理念,支持模型、工具、技能、工作流等全模块自由替换与扩展。其核心Cordis内核实现动态插件管理,赋能Agent自进化。已成GitHub史上增速最快开源项目(15w+ Star),标志着国内大模型从拼价格转向重架构与生态的新拐点。
1283 5
从 0 到 1,DeepSeek Harness 保姆级安装与使用教程!

热门文章

最新文章