HarmonyOS鸿蒙Next中TaskPool创建多线程修改单例对象变量失败问题如何解决

HarmonyOS鸿蒙Next中TaskPool创建多线程修改单例对象变量失败问题如何解决

【问题现象】

在TaskPool里对单例的成员变量进行了修改,主线程读取到的仍然是修改前的值。

【背景知识】

【定位思路】

多线程操作第一印象肯定是ArkTS异步能力TaskPoolWorker

当前ArkTS提供了TaskPool和Worker两种并发能力,TaskPool和Worker都基于Actor并发模型实现。

在Actor并发模型中,不同线程不会使用同一个单例对象。每个线程都有自己独立的内存空间,线程之间通过消息传递机制进行通信,不会直接访问对方的内存空间,在TaskPool中对单例对象的成员变量进行修改后,主线程读取到未修改的值。

TaskPool和worker的多线程方案都是基于Actor并发模型实现的。每个线程都有自己独立的内存空间,修改一个线程的单例对象不会影响到其他线程中的同一个单例对象。

【解决方案】

共享模块可在线程间共享,可以实现在不同线程间操作同一个单例对象,可以使用共享模块开发。

(1)在共享模块中创建单例。

// SharedModule.ets
import { ArkTSUtils } from '@kit.ArkTS';
"use shared" // 启动共享模块

@Sendable
export class TaskHandle {
  private static instance: TaskHandle = new TaskHandle();
  private asyncLock: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock();
  private testNum: number = 0;

  public async testTask(id: number) {
    // 共享异步线程操作同一个数据
    await this.asyncLock.lockAsync(async () => {
      hilog.info(0x0000, 'taskpool', "TaskHandle task id: " + id + ", testNum: " + this.testNum);
      this.testNum++;
      await this.sleep(1000);
      hilog.info(0x0000, 'taskpool', "TaskHandle task id: " + id + ", testNum: " + this.testNum);
    })
  }

  public getTestNum() {
    // 获取测试数据
    return this.testNum;
  }

  public sleep(time: number): Promise<void> {
    // 模拟延时操作
    return new Promise(resolve => setTimeout(resolve, time))
  }

  static getInstance(): TaskHandle {
    // 单例方法
    return TaskHandle.instance;
  }
}

(2)外部调用单例的入口。

// Index.ets
import { ArkTSUtils, taskpool } from '@kit.ArkTS';
import { TaskHandle } from './SharedModule'

export class Test {
  async testTaskpool(): Promise<void> {
    // 启动多个线程执行数据操作
    let task1: taskpool.Task = new taskpool.Task(func, 1);
    let task2: taskpool.Task = new taskpool.Task(func, 2);
    let task3: taskpool.Task = new taskpool.Task(func, 3);

    await taskpool.execute(task1);
    await taskpool.execute(task2);
    await taskpool.execute(task3);

    hilog.info(0x0000, 'taskpool', "test task api: " + TaskHandle.getInstance().getTestNum());
  }
}

@Concurrent
async function func(num: number): Promise<void> {
  // 单个异步线程操作
  await TaskHandle.getInstance().testTask(num);
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Text(this.message)
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .onClick(()=>{
          // 启动线程操作
          func(0)
          let a = new Test()
          a.testTaskpool()
        })
        .width('100%')
    }
    .height('100%')
  }
}

结果如下图所示:

点击放大


更多关于HarmonyOS鸿蒙Next中TaskPool创建多线程修改单例对象变量失败问题如何解决的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中TaskPool创建多线程修改单例对象变量失败问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,使用TaskPool创建多线程修改单例对象变量时,可能会遇到线程安全问题

TaskPool是鸿蒙提供的一种轻量级线程池,用于执行并发任务。由于多线程并发访问单例对象的变量,可能导致数据不一致或修改失败。

要解决这个问题,可以通过以下方式:

  1. 使用锁机制:在修改单例对象变量时,使用鸿蒙提供的TaskDispatcherLock机制来保证线程安全。例如,使用Lock对象在修改变量前加锁,修改完成后释放锁,确保同一时间只有一个线程可以修改变量。

  2. 原子操作:如果变量是基本类型,可以使用鸿蒙提供的原子操作类(如AtomicIntegerAtomicBoolean等)来保证操作的原子性,避免多线程并发修改时的数据竞争问题。

  3. 线程隔离:如果单例对象的变量是线程间共享的,可以考虑使用线程隔离的方式,确保每个线程操作的是自己的副本,而不是直接修改共享变量。

  4. 单线程模型:如果业务逻辑允许,可以将修改单例对象变量的任务放在同一个线程中执行,避免多线程并发修改的问题。

通过以上方法,可以有效解决TaskPool创建多线程修改单例对象变量失败的问题。

回到顶部