1.worker 的创建
首先创建 worker 文件;右键 ->选择 worker即可;创建对应的 worker 文件;
在页面中创建 worker 实例;如下:
private workerPort: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/Worker.ets')
注意:添加的路径是如下格式:[moduleName]/ets/[文件相对路径];同时也支持设置优先级等参数;如下:
new worker.ThreadWorker('entry/ets/workers/Worker.ets',{priority:100,name:'下载 worker',shared:true})
worker子线程:
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
workerPort.onmessage = (event: MessageEvents) => {
// event.data//用于接收宿主发送过来的数据
}
//当工作线程收到无法反串化的消息时调用。事件处理程序在 Worker 线程中执行
workerPort.onmessageerror = (event: MessageEvents) => {
};
//当工作者执行过程中发生异常时调用。事件处理程序在 Worker 线程中执行。
workerPort.onerror = (event: ErrorEvent) => {
};
宿主线程接收 worker子线程返回的数据如下:
aboutToAppear(): void {
this.workerPort.onmessage = (event: MessageEvents) => {
//在这里用于接收子线程传递过来的数据
}
//同样可以通过 workerPort监听错误,退出等监听
}
宿主线程关闭 worker 线程,这个地方是必须的。worker 的线程的生命周期开发者控制;
aboutToDisappear(): void {
try {
this.workerPort.terminate()
} catch (error) {
}
}
子线程或者宿主线程发送数据到对方:
let info = new UserModel('小明', '20')
this.workerPort.postMessage(info)
2.worker实现数据的传递
数据 model 代码
export class UserModel {
name: string
age: string
constructor(name: string, age: string) {
this.name = name
this.age = age
}
printUserInfo() {
console.log('----------- > worker UserModel name ' + this.name + " age " + this.age)
}
}
将数据传递到 worker 子线程:
try {
let info = new UserModel('小明', '20')
this.workerPort.postMessage(info)
} catch (error) {
console.log('----------- > worker page ' + error)
}
worker 子线程接收数据代码如下:
workerPort.onmessage = (event: MessageEvents) => {
try {
const user = event.data as UserModel
console.log('----------- > worker ets ' + JSON.stringify(user))
//直接调用类实例中的方法
user.printUserInfo()
} catch (error) {
console.log('----------- > worker ets catch ' + error)
}
};
结果日志:
----------- > worker ets {"name":"小明","age":"20"}
----------- > worker ets catch TypeError: undefined is not callable
结果同 taskpool 在的子线程执行一致;在子线程传递数据时,采用的是序列化传递方式;相当于仅仅将宿主对象的值给了 worker 子线程类变量,并没有序列化类实例中的方法。因此出现未定义异常;
如何解决上述问题呢?其实就是在 model 上添加注解@Sendable;如下:
@Sendable
export class UserModel {
}
//再次执行结果:
----------- > worker ets {"name":"小明","age":"20"}
----------- > worker UserModel name 小明 age 20
那么在子线程和宿主线程中的对象是一个吗?以及在子线程将数据更改后,宿主线程的数据会更改吗?验证代码如下:
worker 线程:
workerPort.onmessage = (event: MessageEvents) => {
try {
const user = event.data as UserModel
console.log('----------- > worker worker onmessage ' + JSON.stringify(user) + " .... hash " + util.getHash(user).toString())
user.name = '大明'
workerPort.postMessage(user)
} catch (error) {}
//宿主线程
try {
let info = new UserModel('小明', '20')
console.log('----------- > worker page hash ' + util.getHash(info))
this.workerPort.postMessage(info)
setTimeout(() => {
console.log('----------- > worker page timeout ' + json.stringify(info) +".... hash "+util.getHash(info).toString())
}, 1000)
} catch (error) {
console.log('----------- > worker page ' + error)
}
输入日志:
13161-13161 I ----------- > worker page hash 1720174752
13161-13417 I ----------- > worker worker onmessage {"name":"小明","age":"20"} .... hash 1720174752
13161-13161 I ----------- > worker page onmessage {"name":"大明","age":"20"} ... hash 1720174752
13161-13161 I ----------- > worker page timeout {"name":"小明","age":"20"}.... hash 1720174752
结论:在传递添加@Sendable 的 model 时,宿主线程和 worker 线程的 hash值是一样的,这里也就是进行的内存地址传递;
但是在子线程更改值后,主线程的变量值并不会跟随改变;因此想要获取改变后的值,可以通过在宿主线程接收值来监听改变;(这个地方和 java 的内存共享不太一样;也没有搞明白原因)
worker线程中单例使用,以及对应问题:
单例 model
"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()
宿主线程调用:
try {
let info = userInfo
info.name = '小明'
info.age = '20'
console.log('----------- > worker page ' + util.getHash(info).toString())
this.workerPort.postMessage(info)
setTimeout(() => {
console.log('----------- > worker page update ' + json.stringify(info))
}, 1000)
} catch (error) {
console.log('----------- > worker page ' + error)
}
worker 线程
workerPort.onmessage = (event: MessageEvents) => {
try {
const user = event.data as UserInfo
console.log('----------- > worker ets ' + JSON.stringify(user) + " .... " + util.getHash(user).toString())
//直接获取实例
user.name = '大明'
console.log('----------- > worker ets singleton update ' + JSON.stringify(user) )
console.log('----------- > worker ets singleton ' + JSON.stringify(userInfo) + " .... " + util.getHash(userInfo).toString())
} catch (error) {
console.log('----------- > worker ets catch ' + error)
}
}
出现问题日志:
// 如果传递的数据内部包含异步锁,将无法正常传递数据,报如下异常;
BusinessError: An exception occurred during serialization, failed to serialize message.
Serialize error: Serialize don't support object type: NativePointer
原因是在传递的 model 中存在无法序列化的内容;其实就是异步锁:
lock: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock()
没有找到原因:应该是现在鸿蒙还不支持 worker 线程,传递携带异步锁的 model;如果存在需要异步锁的地方尽量使用 taskpool;
注释异步锁代码后执行结果:
5589-5589 I ----------- > worker page 1016019831
5589-5840 I ----------- > worker ets {"name":"小明","age":"20"} .... 1016019831
5589-5840 I ----------- > worker ets singleton update {"name":"大明","age":"20"}
5589-5840 I ----------- > worker ets singleton {"name":"小明","age":"20"} .... 1016019831
5589-5589 I ----------- > worker page update {"name":"小明","age":"20"}
发现在worker中针对 model 设置@Sendable +"use shared" 在子线程更改值后,在主线程的对象是没有更新的,设置延时获取同样没有更新;但是对象的 hash 值却是相同的;
**注意:**这个地方同 taskpool 不一样;在 taskpool 中,在 model 设置了内存共享后,如果子线程更改,在宿主线程值同样改变;
代码如下:
let user = new UserModel('小明', '20')
console.log(`--------- > taskpool 宿主线程 hash ${util.getHash(user)
.toString()} ... ${JSON.stringify(user)}`)
taskpool.execute(verify,user).catch(() => {})
setTimeout(()=>{
console.log(`--------- > taskpool result hash ${util.getHash(user)
.toString()} ... update ${JSON.stringify(user)}`)
},1000)
子线程代码:
@Concurrent
function verify(user: UserModel) {
console.log(`--------- > taskpool Concurrent hash ${util.getHash(user)
.toString()} ... ${JSON.stringify(user)}`)
user.name = '大明'
console.log(`--------- > taskpool Concurrent ... update ${JSON.stringify(user)}`)
}
执行结果:
5480-5480 I --------- > taskpool 宿主线程 hash 1198510185 ... {"name":"小明","age":"20"}
5480-5613 I --------- > taskpool Concurrent hash 1198510185 ... {"name":"小明","age":"20"}
5480-5613 I --------- > taskpool Concurrent ... update {"name":"大明","age":"20"}
5480-5480 I --------- > taskpool result hash 1198510185 ... update {"name":"大明","age":"20"}