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

ops/2024/11/22 14:37:22/

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我们在下篇中介绍。


http://www.ppmy.cn/ops/135805.html

相关文章

大三学生面试经历(2)

继续昨天的内容&#xff0c;我面试的是一个Java实习岗&#xff0c;但是居然也问了我前端&#xff08;vue&#xff09;相关的问题 最大的感觉就是&#xff0c;现在真的越来越卷了&#xff0c;后端都把前端的东西卷完了&#xff0c;当时是线上面试&#xff0c;感觉答的不太好&…

Hello-Go

Hello-Go 环境变量 GOPATH 和 GOROOT &#xff1a;不同于其他语言&#xff0c;go中没有项目的说法&#xff0c;只有包&#xff0c;其中有两个重要的路径&#xff0c;GOROOT 和 GOPATH Go开发相关的环境变量如下&#xff1a; GOROOT&#xff1a;GOROOT就是Go的安装目录&…

【大选】2024年美国总统选举数据分析可视化

前言 • &#x1f453; 可视化主要使用 Plotly • &#x1f50e; 数据处理主要使用 pandas • &#x1f449; 本文是我自己在和鲸社区的原创 1.项目背景描述 2024年美国大选是该国政治生活中的重要事件&#xff0c;吸引了全球的关注。本报告通过对选举数据的分析&#xff0c…

Linux-Apache静态资源

文章目录 静态资源权限设置 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月20日11点21分 静态资源 Apache配置静态资源 可以在网页上配置一个静态的FTP服务器&#xff0c;让用户…

hadoop+wsl 10.255.255.254,BlockMissingException: Could not obtain block: 踩坑

起因&#xff1a; 我在我的wsl中启动一个hadoop 用java客户端连接hadoop&#xff0c;读取某个文件的内容异常&#xff0c;但是读取文件名&#xff0c;文件列表正常 问题&#xff1a; BlockMissingException: Could not obtain block:BP-xxx 显示缺少文件块&#xff0c;因此…

我用豆包MarsCode IDE 做了一个 CSS 权重小组件

作者&#xff1a;夕水 查看效果 作为一个前端开发者&#xff0c;应该基本都会用 VSCode 来做开发&#xff0c;所以也应该见过如下这张图的效果: 以上悬浮面板分为2个部分展示内容。 <element class"hljs-attr">: 代表元素只有一个类名叫hljs-attr的类选择器&am…

使用 npm 安装 Electron 作为开发依赖

好的&#xff0c;下面是一个使用 npm pack 和 npm install 命令来打包和安装离线版本的 npm 包的具体示例。我们将以 electron 为例&#xff0c;演示如何在有网络连接的机器上打包 electron&#xff0c;然后在没有网络连接的机器上安装它。 步骤 1: 在有网络连接的机器上打包 …

计算机网络(第一章)

文章目录 概述1.1计算机网络在信息时代的作用1.2因特网概述1. 网络,互连网(互联网)和因特网2. 因特网发展的三个阶段3.因特网的标准化工作4.因特网的组成 1.3 三种交换方式电路交换 (Circuit Switching)分组交换 (Packet Switching)报文交换 (Message Switching)三个交换的对比…