鸿蒙多线程开发——线程间数据通信对象01

1、线程间通信

线程间通信指的是并发多线程间存在的数据交换行为。由于ArkTS语言兼容TS/JS,其运行时的实现与其它所有的JS引擎一样,都是基于Actor内存隔离的并发模型提供并发能力。

对于不同的数据对象,在ArkTS线程间通信的行为是有差异的,比如普通JS对象、ArrayBuffer对象、SharedArrayBuffer对象等,跨线程的行为是不一致的,包括序列化反序列化拷贝、数据转移、数据共享等不同行为。

以JS对象为例,其在并发任务间的通信采用了标准的Structure Clone算法(序列化反序列化),通过序列化将JS对象转成与引擎无关的数据(字符串或内存块等),在另一个并发实例通过反序列化,还原成与原JS对象内容一致的新对象,因此通常需要经过深拷贝,效率较低。示意如下:

ArkTS目前主要提供两种并发能力支持线程间通信:TaskPool和Worker。之前的文章中我们有做过介绍:鸿蒙多线程开发------TaskPool任务池鸿蒙多线程开发------Worker多线程

在线程间通信时,我们可以使用5类对象用于数据传输,分别是:JS普通对象、ArrayBuffer对象、SharedArrayBuffer对象、Transferable对象(NativeBinding对象)、Sendable对象。

2、JS普通对象

普通对象跨线程时通过拷贝形式传递,两个线程的对象内容一致,但是指向各自线程的隔离内存区间。例如Object、Array、Map等对象是通过这种方式实现跨并发实例通信的。js普通对象比较简单,可以理解为普通的JSON对象即可,通信过程如下图所示:

3、ArrayBuffer对象

ArrayBuffer内部包含一块Native内存。其JS对象壳与普通对象一样,需要经过序列化与反序列化拷贝传递,但是Native内存有两种传输方式:拷贝和转移。

传输时采用拷贝的话,需要经过深拷贝(递归遍历),传输后两个线程都可以独立访问ArrayBuffer。通信过程如下图所示:

如果采用转移的方式,则原线程无法使用此ArrayBuffer对象,跨线程时只需重建JS壳,Native内存无需拷贝,效率更高。通信过程如下图所示:

ArrayBuffer可以用来表示图片等资源,在应用开发中,会遇到需要进行图片处理的场景(比如需要调整一张图片的亮度、饱和度、大小等),为了避免阻塞主线程,可以将图片传递到子线程中执行这些操作。转移方式性能更高,但是原线程不能再访问ArrayBuffer对象,如果两个线程都需要访问,则需要采用拷贝方式,否则建议采用转移方式,提升性能。

下面将分别通过拷贝和转移的方式,将图片传递到子线程中。

👉🏻 ArrayBuffer拷贝传输方式

在ArkTS中,TaskPool传递ArrayBuffer数据时,默认使用转移的方式,通过调用setTransferList()接口,指定对应的部分数据传递方式为转移方式,其余部分数据可以切换成拷贝的方式。

首先,实现一个需要在Task中执行的用于处理ArrayBuffer的接口。

然后,通过拷贝的方式将ArrayBuffer数据传递到Task中,并在Task中处理ArrayBuffer。

最后,主线程接收到Task执行完毕后返回的ArrayBuffer数据,拼接数据展示。

复制代码
// Index.etsimport { taskpool } from '@kit.ArkTS';import { BusinessError } from '@kit.BasicServicesKit';@Concurrentfunction adjustImageValue(arrayBuffer: ArrayBuffer): ArrayBuffer {  // 对arrayBuffer进行操作  return arrayBuffer;  // 返回值默认转移}function createImageTask(arrayBuffer: ArrayBuffer, isParamsByTransfer: boolean): taskpool.Task {  let task: taskpool.Task = new taskpool.Task(adjustImageValue, arrayBuffer);  if (!isParamsByTransfer) { // 是否使用转移方式    // 传递空数组[],全部arrayBuffer参数传递均采用拷贝方式    task.setTransferList([]);  }  return task;}@Entry@Componentstruct Index {  @State message: string = 'Hello World';  build() {    RelativeContainer() {      Text(this.message)        .id('HelloWorld')        .fontSize(50)        .fontWeight(FontWeight.Bold)        .alignRules({          center: { anchor: '__container__', align: VerticalAlign.Center },          middle: { anchor: '__container__', align: HorizontalAlign.Center }        })        .onClick(() => {          let taskNum = 4;          let arrayBuffer = new ArrayBuffer(1024 * 1024);          let taskPoolGroup = new taskpool.TaskGroup();          // 创建taskNum个Task          for (let i: number = 0; i < taskNum; i++) {            let arrayBufferSlice: ArrayBuffer = arrayBuffer.slice(arrayBuffer.byteLength / taskNum * i, arrayBuffer.byteLength / taskNum * (i + 1));            // 使用拷贝方式传入ArrayBuffer,所以isParamsByTransfer为false            taskPoolGroup.addTask(createImageTask(arrayBufferSlice, false));          }          // 执行Task          taskpool.execute(taskPoolGroup).then((data) => {            // 返回结果,对数组拼接,获得最终结果          }).catch((e: BusinessError) => {            console.error(e.message);          })        })    }    .height('100%')    .width('100%')  }}

👉🏻 ArrayBuffer转移传输方式

在TaskPool中,传递ArrayBuffer数据,默认使用转移方式,原线程不能再使用传输给子线程的ArrayBuffer。所以在上文示例的基础上,去除task.setTransferList接口就可以实现,代码如下(注意createImageTask方法的实现和调用):​​​​​​​

复制代码
// Index.etsimport { taskpool } from '@kit.ArkTS';import { BusinessError } from '@kit.BasicServicesKit';@Concurrentfunction adjustImageValue(arrayBuffer: ArrayBuffer): ArrayBuffer {  // 对arrayBuffer进行操作  return arrayBuffer;  // 返回值默认转移}function createImageTask(arrayBuffer: ArrayBuffer): taskpool.Task {  let task: taskpool.Task = new taskpool.Task(adjustImageValue, arrayBuffer);  return task;}@Entry@Componentstruct Index {  @State message: string = 'Hello World';  build() {    RelativeContainer() {      Text(this.message)        .id('HelloWorld')        .fontSize(50)        .fontWeight(FontWeight.Bold)        .alignRules({          center: { anchor: '__container__', align: VerticalAlign.Center },          middle: { anchor: '__container__', align: HorizontalAlign.Center }        })        .onClick(() => {          let taskNum = 4;          let arrayBuffer = new ArrayBuffer(1024 * 1024);          let taskPoolGroup = new taskpool.TaskGroup();          // 创建taskNum个Task          for (let i: number = 0; i < taskNum; i++) {            let arrayBufferSlice: ArrayBuffer = arrayBuffer.slice(arrayBuffer.byteLength / taskNum * i, arrayBuffer.byteLength / taskNum * (i + 1));            taskPoolGroup.addTask(createImageTask(arrayBufferSlice));          }          // 执行Task          taskpool.execute(taskPoolGroup).then((data) => {            // 返回结果,对数组拼接,获得最终结果          }).catch((e: BusinessError) => {            console.error(e.message);          })        })    }    .height('100%')    .width('100%')  }}

由于篇幅原因SharedArrayBuffer、Transferable、Sendable我们在下篇中介绍。

相关推荐
早點睡3905 小时前
高级进阶 React Native 鸿蒙跨平台开发:@react-native-community-slider 滑块组件
react native·react.js·harmonyos
一只大侠的侠5 小时前
Flutter开源鸿蒙跨平台训练营 Day11从零开发商品详情页面
flutter·开源·harmonyos
Mongnewer5 小时前
试写UI界面设计器
ui·界面设计器
一只大侠的侠5 小时前
React Native开源鸿蒙跨平台训练营 Day18自定义useForm表单管理实战实现
flutter·开源·harmonyos
一只大侠的侠5 小时前
React Native开源鸿蒙跨平台训练营 Day20自定义 useValidator 实现高性能表单验证
flutter·开源·harmonyos
听麟6 小时前
HarmonyOS 6.0+ 跨端智慧政务服务平台开发实战:多端协同办理与电子证照管理落地
笔记·华为·wpf·音视频·harmonyos·政务
前端世界7 小时前
从单设备到多设备协同:鸿蒙分布式计算框架原理与实战解析
华为·harmonyos
一只大侠的侠7 小时前
Flutter开源鸿蒙跨平台训练营 Day12从零开发通用型登录页面
flutter·开源·harmonyos
前端不太难8 小时前
HarmonyOS App 工程深水区:从能跑到可控
华为·状态模式·harmonyos
万少9 小时前
端云一体 一天开发的元服务-奇趣故事匣经验分享
前端·ai编程·harmonyos