鸿蒙多线程 taskpool 的简单使用

鸿蒙线程间并发,数据通信的方式采用的是消息通信模式;在 java 中采用的是内存共享模式;这是两者线程间并发的主要区别;

鸿蒙解释文档:文档中心

1.多线程 taskpool 的创建和使用

首先定义一个 function 方法,并在方式上添加@Concurrent注解;这个地方注意function方法创建是在在 struct 外部定义的;代码如下

typescript 复制代码
export class UserModel {
  name: string
  age: string

  constructor(name: string, age: string) {
    this.name = name
    this.age = age
  }


  printUserInfo() {
    console.log('当前 用户的信息是 name ' + this.name + " age " + this.age)
  }
}
java 复制代码
@Concurrent
function showInfo(info: UserModel) {
//方法里面就是执行的子线程代码
return ''//返回子线程执行的结果值
}

开启子线程通过 taskpool ;开启方式有几种方式,一种是通过创建 task,一种是将 task 放到 taskGroup 中,一种是通过taskpool 直接执行;代码如下:

vbnet 复制代码
第一种方式:
let info = new UserModel('小明', '20')
let task = new taskpool.Task(showInfo, info)
taskpool.execute(task).catch(() => {})

第二种方式:
let group = new taskpool.TaskGroup()
try {
   group.addTask(task)
    //  group.addTask(showInfo, info)//或者通过 group添加方法
} catch (error) {}
taskpool.execute(group).catch(() => {})

第三种方案
taskpool.execute(showInfo,info).catch(() => { })

通过上述简单步骤就开启了子线程;

2.taskpool 传递数据;

宿主线程和子线程之间传递数据,在宿主线程调用方法时直接将参数传递即可,宿主线程获取子线程数据,通过返回值获取即可;代码如下;

javascript 复制代码
//调用方法showInfo,传递,这个时候在子线程就能获取到数据
let info = new UserModel('小明', '20')
taskpool.execute(showInfo, info)

//在宿主线程获取到数据
  taskpool.execute(showInfo, info).catch(() => {}).then((result) => {
                //在 then 中就可以获取到子线程返回的数据
        console.log('--------- > taskpool  then .. ' + JSON.stringify(result))
          })

//另一种方式 借助await+async 通过等待的方式获取到子线程返回的结果;
let result = await taskpool.execute(showInfo, info).catch(() => {})

结果如下:

json 复制代码
--------- > taskpool  then ..{"name":"小明","age":"20"}

虽然能够实现宿主线程和子线程之间的数据传递,但是验证会发现宿主线程的对象和主线程的对象并不是同一个;原因:鸿蒙的线程并发采用的是消息通信并发模式;每一个线程都有独立的内存空间;在传递值的时候采用了将 创建对象->数据序列化->反序列化 赋值的操作;也就是在子线程中的对象是新的;同时无法调用类实例中的自定义方法;

鸿蒙官网描述:Actor模型中,不同角色之间并不共享内存,生产者线程和UI线程都有自己的虚拟机实例,两个虚拟机实例之间拥有独占的内存,相互隔离。生产者生产出结果后,通过序列化通信将结果发送给UI线程。UI线程消费结果后,再发送新的生产任务给生产者线程。(文档中心

验证方式通过打印 hashcode值,进行分辨

scss 复制代码
util.getHash(info).toString()

验证代码如下:

javascript 复制代码
let info = new UserModel('小明', '20')
console.log(`--------- > taskpool 宿主线程 hash ${util.getHash(info)
  .toString()} ... ${JSON.stringify(info)}`)
taskpool.execute(userinfo,info).catch(() => {}).then((result)=>{
  if (result) {
      console.log(`--------- > taskpool 宿主线程  hash ${util.getHash(result)
      .toString()} ... ${JSON.stringify(result)}`)
    }
})


//子线程方法
@Concurrent
function userinfo(info:  UserModel) {
  console.log(`--------- > taskpool 子线程 hash ${util.getHash(info)
    .toString()} ... ${JSON.stringify(info)}`) //  1218349983
  info.name = '大明'
  return info
}

打印结果:

bash 复制代码
--------- > taskpool 宿主线程 hash 126564622 ... {"name":"小明","age":"20"}
--------- > taskpool 子线程 hash 1181217180 ... {"name":"小明","age":"20"}
--------- > taskpool 宿主线程  hash 1015499472 ... {"name":"大明","age":"20"}

3.taskpool 实现数据的共享,以及满足类方法 可使用;

为了避免在类实例中创建的方法不可用,以及将对象传递到子线程中经过序列化过程(因此如果大量数据在经过序列化和反序列化是很耗费性能的);可以借助@Sendable注解,进行避免。在引入该注解后,就从传递序列化数据变成了传递指针地址;定义类如下:

typescript 复制代码
@Sendable
export class UserInfo {
  name: string = ''
  age: string = ''
  constructor(name: string, age: string) {
    this.name = name
    this.age = age
  }
  getNameInfo() {
    return `名字 .... ${this.name}`
  }
}

在验证时注意:如果传递的UserInfo 如果没有加@Sendable 时;如果调用的getNameInfo方法,在调用点添加 try catch,避免找不到问题原因;

4.taskpool 子线程中使用单例;

那么在子线程和宿主线程中使用单例模式,能保证单例对象的唯一性吗?

不能;原因还是鸿蒙的多线程并发属于消息通信并发模式;每个线程会有自己的内存空间;

单例在子线程调用的时候,会再次创建一个对象实例;验证代码如下:

javascript 复制代码
//验证用户单例在线程中的使用
@Concurrent
function userSingleton(info: UserInfo) {

  console.log(`--------- > taskpool 单例信息  ${util.getHash(info)
    .toString()} ... ${JSON.stringify(info)}`) 

  //调用新的实例
  let newInfo = userInfo
  console.log(`--------- > taskpool 单例信息  ${util.getHash(newInfo)
    .toString()}  ... info ${JSON.stringify(newInfo)}}`) 
  }

结果:(验证发现,在子线程中调用单例,会重新创建一个新的实例)

json 复制代码
 --------- > taskpool 单例信息  1037896672 ... {"name":"小明","age":"20","money":0,"lock":{}}
 --------- > taskpool 单例信息  740780191  ... info {"name":"","age":"","money":0,"lock":{}}}

单例代码:

typescript 复制代码
@Sendable
export class UserInfo {
  name: string = ''
  age: string = ''

  private static readonly instance: UserInfo = new UserInfo()

  static getInstance() {
    return UserInfo.instance
  }

  private constructor() {
  }
}

export const userInfo: UserInfo = UserInfo.getInstance()

那么如何保证多线程条件下,内存的持有的对象是唯一的呢?其实就是在类上方添加如下字符串 "use shared";代码如下:

kotlin 复制代码
"use shared"//就这个
@Sendable
export class UserInfo {
}

添加后的结果日志:

json 复制代码
--------- > taskpool 单例信息  1198219253 ... {"name":"小明","age":"20","money":0,"lock":{}}
--------- > taskpool 单例信息  1198219253  ... info {"name":"小明","age":"20","money":0,"lock":{}}}

再次通过方法验证就会发现 hashcode 是一致的了;那么既然这么共享内存了,就涉及到另一个问题;就是如何保证内存中数据的安全性,避免多线程并发出现脏数据呢?

其实就是通过锁来避免的,ArkTSUtils.locks.AsyncLock;简易代码如下:

typescript 复制代码
"use shared"
@Sendable
export class UserInfo {
  name: string = ''
  age: string = ''
  money: number = 0
  //添加异步锁
  lock: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock()
  private static readonly instance: UserInfo = new UserInfo()

  static getInstance() {
    return UserInfo.instance
  }

  private constructor() {
  }

  getNameInfo() {
    return `名字 .... ${this.name}`
  }

  addMoney() {
    this.lock.lockAsync(() => {
      this.money++
    }).catch(() => {
    })
  }
}

export const userInfo: UserInfo = UserInfo.getInstance()

验证代码如下:

scss 复制代码
//验证多个线程执行累加任务。是否存在脏数据
@Concurrent
function asyncAdd(info: UserInfo) {
  info.addMoney()
  return userInfo.money
}

//在点击的时候触发
  for (let i = 0; i < 1000; i++) {
    taskpool.execute(asyncAdd, userInfo).catch(() => {
 })
 }
 setTimeout(() => {
  console.log('--------- > taskpool time ' + userInfo.money)
 }, 1000)

结果:

css 复制代码
--------- > taskpool time 1000

5.taskpool 传递容器数据;

其实线程并发传递容器,分为非 collections下的容器,和 collections.下的容器;其中在 collections 下的容器支持地址传递;也就是在子线程更新容器的值,在主线程同样能够相应;验证代码如下:

typescript 复制代码
//传递普通的容器
@Concurrent
function transmitArray(arr: Array<number>) {
  //传递过来的值
  console.log('--------- > taskpool ' + arr.toString())
  arr.push(10)
  console.log('--------- > taskpool push ' + arr.toString())
  return arr
}

//collections 容器
@Concurrent
function transmitArrays(arr: collections.Array<number>) {
  //传递过来的值
  console.log('--------- > taskpool ' + arr)
  try {
    arr.push(10)
  console.log('--------- > taskpool arr ' + arr)
  } catch (error) {}
  return arr
}

调用代码:

typescript 复制代码
 //采用Array进行传递
let arr = Array<number>(0, 1, 2, 3, 4, 5)
 taskpool.execute(transmitArray, arr).catch(() => {
 }).then((result) => {
   //结论:使用 array 进行数据传递。在子线程中,对数据改变后,并无法改变主线程中的数据
   console.log('--------- > taskpool arr ' + arr + " result " + result)
 })


 //采用collections.Array进行参数传递
 let arr = new collections.Array<number>(0, 1, 2, 3, 4, 5)
 taskpool.execute(transmitArrays, arr).catch(() => {
 }).then((result) => {
   //使用collections 下的集合,在更改值后,对应的主线程的集合也会改变
   console.log('--------- > taskpool arr ' + arr + " result " + result)
 })

结果:

sql 复制代码
Array集合执行结果
--------- > taskpool 0,1,2,3,4,5
--------- > taskpool push 0,1,2,3,4,5,10
--------- > taskpool arr 0,1,2,3,4,5 result 0,1,2,3,4,5,10

collections.Array执行结果
--------- > taskpool 0,1,2,3,4,5
--------- > taskpool push 0,1,2,3,4,5,10
--------- > taskpool arr 0,1,2,3,4,5,10 result 0,1,2,3,4,5,10

结论:通过Array 作为参数传递。容器在子线程更改数据后,在宿主线程的容器值并不会改变;通过collections.Array进行传递,宿主线程 的容器会跟随子线程改变而改变;

6.taskpool 实现依赖执行

如果子线程之间需要项目依赖执行,taskpool 也支持现场之间的依赖关系,注意 :添加依赖关系之后,可能出现死锁情况,一定要注意线程的执行顺序;代码如下

javascript 复制代码
  let task = new taskpool.Task(showInfo, info)
  let task2 = new taskpool.Task(update, info)
  task2.addDependency(task)
  taskpool.execute(task).catch(() => {}).then((result) => {})
  taskpool.execute(task2).catch(() => {}).then((result) => {}

如果有问题,各位大佬欢迎指正;

相关推荐
麟听科技17 小时前
HarmonyOS 6.0+ APP智能种植监测系统开发实战:农业传感器联动与AI种植指导落地
人工智能·分布式·学习·华为·harmonyos
前端不太难17 小时前
HarmonyOS PC 焦点系统重建
华为·状态模式·harmonyos
空白诗18 小时前
基础入门 Flutter for Harmony:Text 组件详解
javascript·flutter·harmonyos
lbb 小魔仙18 小时前
【HarmonyOS】React Native实战+Popover内容自适应
react native·华为·harmonyos
motosheep19 小时前
鸿蒙开发(四)播放 Lottie 动画实战(Canvas 渲染 + 资源加载踩坑总结)
华为·harmonyos
左手厨刀右手茼蒿19 小时前
Flutter for OpenHarmony 实战:Barcode — 纯 Dart 条形码与二维码生成全指南
android·flutter·ui·华为·harmonyos
lbb 小魔仙20 小时前
【HarmonyOS】React Native of HarmonyOS实战:手势组合与协同
react native·华为·harmonyos
果粒蹬i21 小时前
【HarmonyOS】React Native实战项目+NativeStack原生导航
react native·华为·harmonyos
waeng_luo21 小时前
HarmonyOS 应用开发 Skills
华为·harmonyos
石去皿21 小时前
分布式原生:鸿蒙架构哲学与操作系统演进的范式转移
分布式·架构·harmonyos