鸿蒙多线程开发——sendable共享容器

embedded/2024/11/27 22:53:43/

1、异步锁机制

在介绍共享容器之前,先介绍异步锁机制。

为了解决多线程并发任务间的数据竞争问题,ArkTS引入了异步锁能力。异步锁可能会被类对象持有,因此为了更方便地在并发实例间获取同一个异步锁对象,AsyncLock对象支持跨线程引用传递。

由于ArkTS语言支持异步操作,阻塞锁容易产生死锁问题,因此在ArkTS中仅支持异步锁(非阻塞式锁)。同时,异步锁还可以用于保证单线程内的异步任务时序一致性,防止异步任务时序不确定导致的同步问题。

使用异步锁的方法需要标记为async,调用方需要使用await修饰,才能保证时序正确。

为了解决@Sendable共享对象在不同线程修改共享变量导致的竞争问题,可以采用异步锁进行数据保护。Sendable共享对象在之前文章中有介绍👉🏻鸿蒙多线程开发——线程间数据通信对象03(sendable)

异步锁使用示例如下(关注9~11行,15~17行):

import { ArkTSUtils, taskpool } from '@kit.ArkTS';@Sendableexport class A {  private count_: number = 0;  lock_: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock();  public async getCount(): Promise<number> {    return this.lock_.lockAsync(() => {      return this.count_;    })  }  public async increaseCount() {    await this.lock_.lockAsync(() => {      this.count_++;    })  }}@Concurrentasync function printCount(a: A) {  console.info("InputModule: count is:" + await a.getCount());}@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(async () => {          let a: A = new A();          await taskpool.execute(printCount, a);        })    }    .height('100%')    .width('100%')  }}

2、共享容器

ArkTS共享容器(@arkts.collections (ArkTS容器集))是一种在并发任务间共享传输的容器类,可以用于并发场景下的高性能数据传递。

ArkTS共享容器在多个并发任务间传递时,其默认行为是引用传递,支持多个并发任务可以操作同一个容器实例。另外,也支持拷贝传递,即每个并发任务持有一个ArkTS容器实例。

ArkTS共享容器并不是线程安全的,内部使用了fail-fast(快速失败)机制,即当检测多个并发实例同时对容器进行结构性改变时,会触发异常。因此,在容器内修改属性的场景下,我们需要使用ArkTS提供的异步锁机制保证ArkTS容器的安全访问。

ArkTS共享容器包含如下几种:Array、Map、Set、TypedArray(Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、Uint32Array、Uint8ClampedArray、Float32Array)、ArrayBuffer等。

📢 注意,这里说的容器是@arkts.collections中创建的ArkTs容器(多线程共享容器),并非平时普通使用的容器。

一个Array容器的使用案例如下:​​​​​​​

import { ArkTSUtils, collections, taskpool } from '@kit.ArkTS';@Concurrentasync function add(arr: collections.Array<number>, lock: ArkTSUtils.locks.AsyncLock) { await lock.lockAsync(() => {  // 如果不添加异步锁,任务会因为数据竞争冲突,导致抛异常失败   arr[0]++; })}@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 taskGroup = new taskpool.TaskGroup();          let lock = new ArkTSUtils.locks.AsyncLock();          let arr = collections.Array.create<number>(1, 0);          let count = 1000;          while (count--) {            taskGroup.addTask(add, arr, lock);          }          taskpool.execute(taskGroup).then(() => {            console.info(`Return success: ${arr[0]} === ${count}`);          }).catch((e: Error) => {            console.error("Return error.");          })        })    }    .height('100%')    .width('100%')  }}

3、共享容器注意事项

在@arkts.collections (ArkTS容器集)中提供的容器与前端开发中JS所使用的容器大体上保持一致。但有一部分存在差异。下面对这些差异做一些归类。

📢📢 注意,这里说的容器是@arkts.collections中创建的ArkTs容器(多线程共享容器),并非平时普通使用的容器。

有差异的有:Array、Map、Set、TypedArray。(ArrayBuffer没有差异)

其中TypedArray是以下几种类型的统称:

    • Int8Array、Uint8Array、Uint8ClampedArray

    • Int16Array、Uint16Array

    • Int32Array、Uint32Array、Float32Array

3.1、Array与JS原生API的差异

支持原生容器Array通过collections.Array.from方法转换为ArkTS Array容器;支持通过原生容器Array的from方法将ArkTS Array容器转换为原生容器Array。

有差异的部分罗列如下:

原生API方法ArkTS容器集方法在ArkTS容器中的差异表现
length: numberreadonly length: number为了防止undefined扩散,不允许设置length。
new(arrayLength ?: number): any[]static create(arrayLength: number, initialValue: T): Array为了防止undefined扩散,构造函数中必须提供一个初始值的构造函数。
new <T>(...items: T[]): T[]constructor(first: T, ...left: T[])为了防止undefined扩散,构造函数中必须提供一个初始值的构造函数,继承场景下,无法调用该函数进行对象构造。
pop(): T | undefinedpop(): T | undefined不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
push(...items: T[]): numberpush(...items: T[]): number不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
concat(...items: ConcatArray<T>[]): T[]concat(...items: ConcatArray<T>[]): Array<T>不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
concat(...items: (T | ConcatArray<T>)[]): T[]concat(...items: ConcatArray<T>[]): Array<T>不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
shift(): T | undefinedshift(): T | undefined不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
sort(compareFn?: (a: T, b: T) => number): thissort(compareFn?: (a: T, b: T) => number): Array<T>1. 不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。2. 继承场景下,无法获得实际类型的返回值。
unshift(...items: T[]): numberunshift(...items: T[]): number不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): voidforEach(callbackFn: (value: T, index: number, array: Array<T>) => void): voidArkTS不支持this,因此不支持thisArg参数。
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]map<U>(callbackFn: (value: T, index: number, array: Array<T>) => U): Array<U>ArkTS不支持this,因此不支持thisArg参数。
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]filter(predicate: (value: T, index: number, array: Array<T>) => boolean): Array<T>ArkTS不支持this,因此不支持thisArg参数。
[n: number]: T[index: number]: T不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。
findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): numberfindIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean): numberArkTS不支持this,因此不支持thisArg参数。
fill(value: T, start?: number, end?: number): thisfill(value: T, start?: number, end?: number): Array<T>1. 不允许在遍历、访问过程中进行元素的增、删、改操作,否则会抛出异常。2. 继承场景下,无法获得实际类型的返回值。

3.2、TypedArray与JS原生API的差异

支持原生容器TypedArray通过collections.TypedArray.from方法转换为ArkTS TypedArray容器;

支持通过原生容器TypedArray的from方法将ArkTS TypedArray容器转换为原生容器TypedArray。

以Int8Array为例,有差异部分罗列如下:

原生API方法ArkTS容器集方法在ArkTS容器中的差异表现
copyWithin(target: number, start: number, end?: number): thiscopyWithin(target: number, start: number, end?: number): Int8Array不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
every(predicate: (value: number, index: number, array: Int8Array) => unknown, thisArg?: any): booleanevery(predicate: TypedArrayPredicateFn<number, Int8Array>): boolean1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
fill(value: number, start?: number, end?: number): thisfill(value: number, start?: number, end?: number): Int8Array不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
filter(predicate: (value: number, index: number, array: Int8Array) => any, thisArg?: any): Int8Arrayfilter(predicate: TypedArrayPredicateFn<number, Int8Array>): Int8Array1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
find(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): number | undefinedfind(predicate: TypedArrayPredicateFn<number, Int8Array>): number | undefined1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
findIndex(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): numberfindIndex(predicate: TypedArrayPredicateFn<number, Int8Array>): numberArkTS不支持this,因此不支持thisArg参数。
forEach(callbackfn: (value: number, index: number, array: Int8Array) => void, thisArg?: any): voidforEach(callbackFn: (value: number, index: number, array: Int8Array) => void): void1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
map(callbackfn: (value: number, index: number, array: Int8Array) => number, thisArg?: any): Int8Arraymap(callbackFn: TypedArrayForEachCallback<number, Int8Array>): Int8Array1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
set(array: ArrayLike<number>, offset?: number): voidset(array: ArrayLike<number>, offset?: number): void不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
some(predicate: (value: number, index: number, array: Int8Array) => unknown, thisArg?: any): booleansome(predicate: TypedArrayPredicateFn<number, Int8Array>): booleanArkTS不支持this,因此不支持thisArg参数。
sort(compareFn?: (a: number, b: number) => number): thissort(compareFn?: TypedArrayCompareFn<number>): Int8Array1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. 继承场景下,无法获得实际类型的返回值。
subarray(begin?: number, end?: number): Int8Arraysubarray(begin?: number, end?: number): Int8Array不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
[index: number]: number[index: number]: number不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
from<T>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => number, thisArg?: any): Int8Arraystatic from<T>(arrayLike: ArrayLike<T>, mapFn: TypedArrayFromMapFn<T, number>): Int8ArrayArkTS不支持this,因此不支持thisArg参数。
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int8Arraystatic from(arrayLike: Iterable<number>, mapFn?: TypedArrayFromMapFn<number, number>): Int8ArrayArkTS不支持this,因此不支持thisArg参数。

3.3、Map与JS原生API的差异

差异部分罗列如下:

原生API方法ArkTS容器集方法在ArkTS容器中的差异表现
readonly size: numberreadonly size: number不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
clear(): voidclear(): void不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
delete(key: K): booleandelete(key: K): boolean不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): voidforEach(callbackFn: (value: V, key: K, map: Map<K, V>) => void): void1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
get(key: K): V | undefinedget(key: K): V | undefined不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
has(key: K): booleanhas(key: K): boolean不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
set(key: K, value: V): thisset(key: K, value: V): Map<K, V>不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
new <K, V>(entries?: readonly (readonly [K, V])[] | null): Map<K, V>constructor(entries?: readonly (readonly [K, V])[] | null)构造时传入的k,v键值不能是非Sendable数据,否则编译会报错。

3.4、Set与JS原生API的差异

差异部分罗列如下:

原生API方法ArkTS容器集方法在ArkTS容器中的差异表现
readonly size: numberreadonly size: numberSendable类和接口中不允许使用计算属性名称(arkts-sendable-compated-prop-name)。
add(value: T): thisadd(value: T): Set<T>不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
clear(): voidclear(): void不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
delete(value: T): booleandelete(value: T): boolean不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): voidforEach(callbackFn: (value: T, value2: T, set: Set<T>) => void): void1. 不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。2. ArkTS不支持this,因此不支持thisArg参数。
has(value: T): booleanhas(value: T): boolean不允许在遍历、访问过程中进行元素的增、删、改操作否则会抛出异常。
values(): IterableIterator<T>values(): IterableIterator<T>Sendable类和接口中不允许使用计算属性名称(arkts-sendable-compated-prop-name)。
new <T = any>(values?: readonly T[] | null): Set<T>constructor(values?: readonly T[] | null)构造时传入数据不能是非Sendable数据,否则编译会报错。


http://www.ppmy.cn/embedded/141024.html

相关文章

vue3 发送 axios 请求时没有接受到响应数据

<script setup> import Edit from ./components/Edit.vue import axios from axios import { onMounted,ref } from vue// TODO: 列表渲染 //装数据的列表 const list ref([]) const count ref(0) const getList async () > {//通过发送 /list 请求从后端拿到列表数…

LSA详情与特殊区域

LSA是构成LSDB的重要原材料&#xff0c;在OSPF中发挥很大作用。 报文 通用头部 LS age&#xff1a;LSA寿命&#xff0c;0-3600s Options&#xff1a;可选项 LS type&#xff1a;LSA类型&#xff0c;三要素之一 Link State ID&#xff1a;LSAID 三要素之一 Advertising Ro…

卸载snap docker一直卡住:Save data of snap “docker“ in automatic snapshot set #3

在卸载 Snap 安装的 Docker 时卡住&#xff0c;通常是因为 Snap 在执行卸载时会先尝试保存一些快照&#xff08;自动或手动创建的&#xff09;&#xff0c;并且该过程可能因某些原因而卡住。为了解决这个问题&#xff0c;你可以按照以下步骤强制删除 Snap 安装的 Docker&#x…

java操作doc——java利用Aspose.Words操作Word文档并动态设置单元格合并

在实际工作中&#xff0c;如果业务线是管理类项目或者存在大量报表需要导出的业务时&#xff0c;可以借助第三方插件实现其对应功能。 尤其是需要对word文档的动态操作或者模板数据的定向合并&#xff0c;使用Aspose会相对来说容易一些&#xff0c;而且相关文档比较完整&#…

开发一套ERP 第三弹 前端构建

初步确定 差不多就套用 pnpm config set registry https://registry.npmmirror.com/ pnpm 配置国内镜像源

Docker 的优势和劣势分别是什么

Docker 在现代软件开发与部署领域意义非凡&#xff0c;有着多方面的优势。在环境一致性方面表现卓越&#xff0c;软件开发各环节常因环境差异产生兼容性故障&#xff0c;而 Docker 把应用及其依赖封装成容器&#xff0c;无论在哪运行&#xff0c;环境都相同&#xff0c;大大减少…

Web 学习笔记 - 网络安全

前言 作为 前端开发者&#xff0c;了解一点 Web 安全方面的基本知识是有很必要的&#xff0c;未必就要深入理解。本文主要介绍常见的网络攻击类型&#xff0c;不作深入探讨。 正文 网络攻击的形式种类繁多&#xff0c;从简单的网站敏感文件扫描、弱口令暴力破解&#xff0c;…

面试学习准备

根据面试题web前端面试 - 面试官系列 里面的题目学习巩固。 1.vue2 组件通信 EventBus&#xff1a; 讲解 全局事件总线&#xff0c;核心思想是通过发布-订阅模式来实现组件之间的通信 在 Vue 2 中&#xff0c;可以直接使用 Vue 实例作为 EventBus。 使用方法&#xff1a;在…