1、异步锁机制

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

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

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

为了解决@Sendable共享对象在不同线程修改共享变量导致的竞争问题,可以采用异步锁进行数据保护。 

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

import { ArkTSUtils, taskpool } from '@kit.ArkTS';
@Sendable
export 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_++;
    })
  }
}
@Concurrent
async function printCount(a: A) {
  console.info("InputModule: count is:" + await a.getCount());
}
@Entry
@Component
struct 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';
@Concurrent
async function add(arr: collections.Array<number>, lock: ArkTSUtils.locks.AsyncLock) {
 await lock.lockAsync(() => {  // 如果不添加异步锁,任务会因为数据竞争冲突,导致抛异常失败
   arr[0]++;
 })
}
@Entry
@Component
struct 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。

有差异的部分罗列如下:

3.2、TypedArray与JS原生API的差异 

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

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

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

3.3、Map与JS原生API的差异

差异部分罗列如下:

3.4、Set与JS原生API的差异

差异部分罗列如下:

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐