文档管理中心

Sendable对象简介

在传统JS引擎中,要优化对象的并发通信开销,唯一的方法是将实现下沉到Native侧,通过Transferable对象的转移或共享来降低开销。然而,开发者仍有大量对象并发通信的需求,这个问题在业界JS引擎中尚未解决。

ArkTS提供了Sendable对象类型,它是一种可在ArkTS并发实例间安全共享和传递的数据类型,它支持引用传递来减少通信成本。

Sendable对象可共享,跨线程前后指向同一个JS对象。如果Sendable对象通过调用Napi接口与一个Native对象绑定,当共享传递Sendable对象时,其绑定的Native对象也会一并共享传递。通信过程如下图所示:

与其它ArkTS数据对象不同,符合Sendable协议的数据对象在运行时应为类型固定的对象。

当多个并发实例尝试同时更新Sendable数据时,会发生数据竞争,例如ArkTS共享容器的多线程操作。因此,ArkTS提供异步锁机制来避免不同并发实例间的数据竞争,并提供了异步等待机制来控制多线程处理数据的时序。同时,还可以通过对象冻结接口将对象冻结为只读,从而避免数据竞争。

Sendable对象提供了并发实例间高效的通信能力,即引用传递,适用于开发者自定义大对象需要线程间通信的场景,例如子线程读取数据库数据并返回给宿主线程,具体代码实现可参考跨并发实例传输大数据场景

基础概念

Sendable协议

Sendable协议定义了ArkTS的可共享对象体系及其规格约束。符合Sendable协议的数据(以下简称Sendable数据)可以在ArkTS并发实例间传递。

默认情况下,Sendable数据在ArkTS并发实例间(包括UI主线程、TaskPool线程、Worker线程)采用引用传递方式,同时还支持拷贝传递方式。

ISendable

在ArkTS语言基础库@arkts.lang中引入了interface ISendable,没有任何方法或属性。ISendable是所有Sendable类型(除了null和undefined)的父类型。ISendable主要用于开发者自定义Sendable数据结构的场景中。类装饰器@Sendable装饰器是implements ISendable的语法糖。

Sendable class

说明

从API version 11开始,支持使用@Sendable装饰器校验Sendable class。

Sendable class需同时满足以下两个规则:

  1. 针对API version 22以前的工程,当且仅当被标注了@Sendable装饰器。从API version 22开始,Sendable class除了必须标注@Sendable装饰器之外,开发者可根据需要在Sendable class上叠加使用其他自定义装饰器,具体操作可参考在Sendable class上叠加其他自定义装饰器

  2. 需满足Sendable约束,详情可查Sendable使用规则

Sendable function

说明
  • 针对API version 12的工程,开发者使用@Sendable装饰器校验Sendable function时,需在工程中配置"compatibleSdkVersionStage": "beta3",否则其Sendable特性将不生效。参考build-profile.json5配置文件说明

  • 针对API version大于12的工程,可直接使用@Sendable装饰器校验Sendable function,无需再进行其他配置。

Sendable function需同时满足以下两个规则:

  1. 当且仅当被标注了@Sendable装饰器

  2. 需满足Sendable约束,具体可参考Sendable使用规则

Sendable interface

Sendable interface需同时满足以下两个规则:

  1. 当且仅当是ISendable或者继承了ISendable。

  2. 需满足Sendable约束,具体可参考Sendable使用规则

Sendable支持的数据类型

说明
  • JS内置对象在并发实例间传递时遵循结构化克隆算法,跨线程行为是拷贝传递。因此,JS内置对象的实例不是Sendable类型。

  • 对象字面量和数组字面量在并发实例间传递时遵循结构化克隆算法,跨线程行为是拷贝传递。因此,对象字面量和数组字面量不是Sendable类型。

Sendable支持const enum类型使用示例:

收起
自动换行
深色代码主题
复制
  1. export const enum ModelState {
  2. ACTIVE,
  3. INACTIVE
  4. }
收起
自动换行
深色代码主题
复制
  1. import { taskpool } from '@kit.ArkTS';
  2. import { ModelState } from './Test';
  3. @Sendable
  4. class Model {
  5. public state: ModelState = ModelState.ACTIVE;
  6. getState() {
  7. console.info('model state is ' + this.state);
  8. }
  9. setState(state: ModelState) {
  10. this.state = state;
  11. }
  12. }
  13. @Concurrent
  14. function setModelState(model: Model) {
  15. model.setState(ModelState.INACTIVE);
  16. model.getState();
  17. }
  18. @Entry
  19. @Component
  20. struct enumusage {
  21. @State message: string = 'Hello World';
  22. @State num: number = 0;
  23. build() {
  24. RelativeContainer() {
  25. Text(this.message)
  26. .id('HelloWorld')
  27. .fontSize(50)
  28. .fontWeight(FontWeight.Bold)
  29. .alignRules({
  30. center: { anchor: '__container__', align: VerticalAlign.Center },
  31. middle: { anchor: '__container__', align: HorizontalAlign.Center }
  32. })
  33. .onClick(async () => {
  34. let model = new Model();
  35. model.getState();
  36. let task = new taskpool.Task(setModelState, model);
  37. await taskpool.execute(task);
  38. this.message = 'success';
  39. })
  40. }
  41. .height('100%')
  42. .width('100%')
  43. }
  44. }

Sendable的实现原理

为了实现Sendable数据在不同并发实例间的引用传递,Sendable共享对象分配在共享堆中,实现跨并发实例的内存共享。

共享堆(SharedHeap)是进程级别的堆空间,与虚拟机本地堆(LocalHeap)不同,LocalHeap仅限单个并发实例访问,而SharedHeap可被所有线程访问。Sendable对象的跨线程行为为引用传递,因此,一个Sendable对象可能被多个并发实例引用。判断该Sendable对象是否存活,取决于所有并发实例是否存在对此Sendable对象的引用。

SharedHeap与LocalHeap关系图

各个并发实例的LocalHeap是隔离的。SharedHeap是进程级别的堆,可以被所有并发实例共享,但SharedHeap不能引用LocalHeap中的对象。

@Sendable装饰器

声明并校验Sendable class和Sendable function。

展开
@Sendable装饰器 说明
装饰器参数 无。
使用场景限制 仅支持在Stage模型的.ets文件中使用。
装饰的函数类型限制 仅支持装饰普通function和Async function类型。
装饰的类继承关系限制 Sendable class只能继承Sendable class,普通Class不可继承Sendable class。
装饰的对象内的属性类型限制

1. 支持string、number、boolean、bigint、null、undefined、const enum、Sendable class、collections容器集、ArkTSUtils.locks.AsyncLock、ArkTSUtils.SendableLruCache、ArkTSUtils.locks.ConditionVariable以及自定义的Sendable函数类型。

2. 禁止使用闭包变量,定义在顶层的Sendable class和Sendable function除外。

3. 不支持通过#定义私有属性,需用private。

4. 不支持计算属性。

5. 不支持类型别名。

装饰的对象内的属性的其他限制

1. 成员属性必须显式初始化,不能使用感叹号。

2. 不支持增加或删除属性,允许修改属性,修改前后属性的类型必须一致,不支持修改方法。

装饰的函数或类对象内的方法参数限制 允许使用local变量、入参和通过import引入的变量。禁止使用闭包变量,但定义在顶层的Sendable class和Sendable function除外。从API version 18开始,支持访问本文件导出的变量。
适用场景

1. 在TaskPool或Worker中使用类方法或Sendable函数。

2. 传输对象数据量较大的场景。序列化耗时会随着数据量增大而增大,使用Sendable对数据进行改造后,传输100KB数据效率提升约20倍,传输1MB数据效率提升约100倍。

装饰器修饰Class使用示例:

收起
自动换行
深色代码主题
复制
  1. @Sendable
  2. class SendableTestClass {
  3. desc: string = 'sendable: this is SendableTestClass ';
  4. num: number = 5;
  5. printName() {
  6. console.info(`sendable: SendableTestClass desc is: ${this.desc}`);
  7. }
  8. get getNum(): number {
  9. return this.num;
  10. }
  11. }
  12. let object = new SendableTestClass();
  13. export { object }

装饰器修饰Function使用示例:

收起
自动换行
深色代码主题
复制
  1. @Sendable
  2. type SendableFuncType = () => void;
  3. @Sendable
  4. class TopLevelSendableClass {
  5. num: number = 1;
  6. printNum() {
  7. console.info('Top level sendable class');
  8. }
  9. }
  10. @Sendable
  11. function topLevelSendableFunction() {
  12. console.info('Top level sendable function');
  13. }
  14. @Sendable
  15. function sendableTestFunction() {
  16. const topClass = new TopLevelSendableClass(); // 顶层sendable class
  17. topClass.printNum();
  18. topLevelSendableFunction(); // 顶层sendable function
  19. console.info('Sendable test function');
  20. }
  21. @Sendable
  22. class SendableTestClass {
  23. constructor(func: SendableFuncType) {
  24. this.callback = func;
  25. }
  26. callback: SendableFuncType; // 顶层sendable function
  27. callSendableFunc() {
  28. sendableTestFunction(); // 顶层sendable function
  29. }
  30. }
在 指南 中进行搜索
请输入您想要搜索的关键词