@Sendable 类 vs 普通 class:ArkTS 跨线程编程的正确姿势
·
@Sendable 类 vs 普通 class:ArkTS 跨线程编程的正确姿势
前言
HarmonyOS 的 ArkTS 采用线程隔离模型:UI 主线程、TaskPool 工作线程、Worker 各自独立,普通的对象实例不能跨线程传递。很多开发者第一次用 taskpool.execute 把对象当参数传进去,直接报错"object is not sendable"。本文基于论坛问题「ArkTS 里 @Sendable 类和普通 class 在 ArkCompiler 有何不同」,讲清 @Sendable 的本质与约束。
问题描述
- 把普通
class实例作为参数传给taskpool.execute/ Worker,运行时报错; - 以为
@Sendable是"引用共享",改了一边另一边也变,结果并没有; - 在
@Sendable类的方法里用了闭包/外部变量,编译直接报错; - 不清楚哪些字段类型可以用、哪些不行。
细节解析
1. 线程隔离是前提
ArkTS 不支持线程间共享内存。一个对象只在它被创建的线程里有效,跨线程必须序列化传递。普通 class 没有"可序列化"的编译期保证,因此禁止跨线程。
2. @Sendable:编译期保证"可跨线程"
@Sendable 装饰的类,编译器会强制约束其成员满足可序列化要求(字段类型必须是 Sendable 兼容类型、方法不能捕获外部变量、不能继承等)。满足后,它就可以作为参数传给 TaskPool/Worker,传递时按值拷贝到目标线程。
3. 值语义,不是引用共享
这是最容易误解的点:@Sendable 实例跨线程是拷贝,目标线程改了它,原线程的那份不会变。如果需要真正共享状态,要用 SharedArrayBuffer、或借助分布式/共享存储,而不是指望对象本身共享。
4. 约束清单
- 字段只能是用 Sendable 兼容类型(number/string/boolean/@Sendable class/Array 等);
- 不能有非 Sendable 实例字段;
- 方法不能引用外层作用域变量(即不能有闭包);
- 不支持继承(extends 受限);
- 构造函数需给字段赋初值。
示例代码
// 可跨线程的点
@Sendable
class Point {
x: number = 0;
y: number = 0;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
move(dx: number, dy: number): void {
this.x += dx; // 注意:只改当前线程的副本
this.y += dy;
}
}
import { taskpool } from '@kit.ArkTS';
@Concurrent
function shiftPoint(p: Point): Point {
p.move(10, 10);
return p; // 返回的是目标线程里的副本
}
// 使用
const p = new Point(0, 0);
taskpool.execute(shiftPoint, p).then((res: Point) => {
console.info('worker 里变成:', res.x, res.y); // 10, 10
console.info('主线程原对象仍是:', p.x, p.y); // 0, 0(未被共享修改)
});
总结
- 普通
class实例不能跨线程;@Sendableclass 可以,前提是满足编译期约束; @Sendable是值拷贝语义,跨线程后两端各自独立,改一边不影响另一边;- 方法不能有闭包、字段必须 Sendable 兼容、不支持继承;
- 真正需要共享状态请用
SharedArrayBuffer或外部存储,而非依赖对象引用。
理解"隔离 + 值拷贝",就不会再被 not sendable 和"改了不生效"困扰。
更多推荐



所有评论(0)