多线程任务
介绍
本示例通过@ohos.taskpool和@ohos.worker接口,展示了如何启动worker线程和taskpool线程。
效果预览
使用说明
-
在主界面,可以点击字符串排序和拷贝文件按钮进入对应的界面;
-
点击字符串排序按钮进入多线程界面:
worker:
- 选择Worker页签,输入待排序的字符串,并以逗号分割。
- 点击字符串排序按钮,会将排序前的字符串发送给worker线程,在worker线程实现字符串排序,然后将排序后的字符串发送给主线程,主线程中显示排序后的字符串。
- 点击清除按钮,清除字符串。
taskpool:
- 选择TaskPool页签,输入待排序的字符串,并以逗号分割。
- 点击立即执行按钮,任务执行完成后将排序后的字符串显示出来。
- 点击超时3s执行按钮,任务延迟3s后执行,执行完成后将排序后的字符串显示出来。
- 点击函数任务按钮,直接调用执行操作,执行完成后将排序后的字符串显示出来。需要注意的是,通过函数任务创建的task任务不支持取消。
- 点击取消任务按钮,会取消最后一个未执行的task任务。需要注意的是,只有当任务数大于最大线程数且任务未开始执行时才可以取消成功。
- 点击清除按钮,清除字符串。
-
点击拷贝文件按钮进入文件拷贝界面:
选择需要拷贝的文件,然后点击拷贝文件按钮,文件拷贝成功,触发事件日志显示沙箱下文件个数以及显示部分拷贝成功的文件名。
工程目录
├──entry/src/main/ets // 代码区
│ ├──common
│ │ ├──Common.ets // 公共工具类
│ │ └──Logger.ets // 日志工具类
│ ├──component
│ │ ├──TaskPoolTab.ets // taskpool页签
│ │ └──WorkerTab.ets // worker页签
│ ├──entryability
│ │ └──EntryAbility.ets
│ ├──model
│ │ ├──MyWorker.ets // 批量拷贝文件方法类
│ │ ├──TaskPoolTab.ts // taskpool页签
│ │ └──WorkerTab.ts // worker页签
│ └──pages
│ ├──CopyFile.ets // 拷贝文件页面
│ ├──TaskPoolTab.ets // taskpool页签
│ └──WorkerTab.ets // worker页签
└──entry/src/main/resources // 应用资源目录
具体实现
- worker页签的实现在字符串排序页面调用,源码参考[StrSort.ets]
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WorkerTab } from '../component/WorkerTab';
import { TaskPoolTab } from '../component/TaskPoolTab';
@Entry
@Component
struct Index {
private controller: TabsController = new TabsController();
@State index: number = 0;
@Builder
tabJsWorker() {
Column() {
Text("Worker")
.width("57vp")
.height("22vp")
.position({ x: "0vp", y: "17vp" })
.fontFamily("HarmonyHeiTi-Medium")
.fontSize(16)
.fontColor(this.index === 0 ? "#007DFF" : "#182431")
.textAlign(TextAlign.Center)
.lineHeight(22)
.fontWeight(this.index === 0 ? 500 : 400)
.opacity(this.index === 0 ? 1 : 0.6)
Line()
.width("57vp")
.height("2vp")
.position({ x: "0", y: "46vp" })
.backgroundColor(this.index === 0 ? "#007DFF" : "linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
}
.id("tabJsWorker")
.width("100%")
.height("100%")
.position({ x: "65%", y: "0vp" })
.onClick(() => {
this.index = 0;
this.controller.changeIndex(this.index);
})
}
@Builder
tabTaskPool() {
Column() {
Text("TaskPool")
.width("68vp")
.height("22vp")
.position({ x: "10vp", y: "17vp" })
.fontFamily("HarmonyHeiTi-Medium")
.fontSize(16)
.fontColor(this.index === 1 ? "#007DFF" : "#182431")
.textAlign(TextAlign.Center)
.lineHeight(22)
.fontWeight(this.index === 1 ? 500 : 400)
.opacity(this.index === 1 ? 1 : 0.6)
Line()
.width("68vp")
.height("2vp")
.position({ x: "10vp", y: "46vp" })
.backgroundColor(this.index === 1 ? "#007DFF" : "linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
}
.id("tabTaskPool")
.height("100%")
.width("100%")
.position({ x: "4%", y: "0" })
.onClick(() => {
this.index = 1;
this.controller.changeIndex(this.index);
})
}
build() {
Row() {
Column() {
Text("ConcurrentModule")
.width("100%")
.height("41vp")
.position({ x: "7%", y: "31vp" })
.fontColor("#182431")
.fontSize("30fp")
.fontFamily("HarmonyHeiTi-Bold")
.lineHeight(41)
.fontWeight(700)
.textAlign(TextAlign.Start)
Tabs({
barPosition: BarPosition.Start,
controller: this.controller
}) {
TabContent() {
WorkerTab();
}
.width("100%")
.height("100%")
.tabBar(this.tabJsWorker)
TabContent() {
TaskPoolTab();
}
.width("100%")
.height("100%")
.tabBar(this.tabTaskPool)
}
.width("100%")
.height("696vp")
.barWidth("100%")
.barHeight("56vp")
.position({ x: "0vp", y: "80vp" })
.padding({ bottom: "24vp" })
.backgroundImage("linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
.barMode(BarMode.Fixed)
.onChange((index: number) => {
this.index = index;
})
}
.backgroundColor("#f1f3f5")
.width("100%")
.height("100%")
}
.width("100%")
.height("100%")
}
}
-
字符串排序:通过调用executeWorkerFunc()创建一个worker线程,把待排序字符串发送给worker线程,等worker线程排序完成后再把结果返回。
-
清除:把字符串输入框和结果都清除。
-
taskpool页签的实现在字符串排序页面调用,源码参考[StrSort.ets]
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WorkerTab } from '../component/WorkerTab';
import { TaskPoolTab } from '../component/TaskPoolTab';
@Entry
@Component
struct Index {
private controller: TabsController = new TabsController();
@State index: number = 0;
@Builder
tabJsWorker() {
Column() {
Text("Worker")
.width("57vp")
.height("22vp")
.position({ x: "0vp", y: "17vp" })
.fontFamily("HarmonyHeiTi-Medium")
.fontSize(16)
.fontColor(this.index === 0 ? "#007DFF" : "#182431")
.textAlign(TextAlign.Center)
.lineHeight(22)
.fontWeight(this.index === 0 ? 500 : 400)
.opacity(this.index === 0 ? 1 : 0.6)
Line()
.width("57vp")
.height("2vp")
.position({ x: "0", y: "46vp" })
.backgroundColor(this.index === 0 ? "#007DFF" : "linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
}
.id("tabJsWorker")
.width("100%")
.height("100%")
.position({ x: "65%", y: "0vp" })
.onClick(() => {
this.index = 0;
this.controller.changeIndex(this.index);
})
}
@Builder
tabTaskPool() {
Column() {
Text("TaskPool")
.width("68vp")
.height("22vp")
.position({ x: "10vp", y: "17vp" })
.fontFamily("HarmonyHeiTi-Medium")
.fontSize(16)
.fontColor(this.index === 1 ? "#007DFF" : "#182431")
.textAlign(TextAlign.Center)
.lineHeight(22)
.fontWeight(this.index === 1 ? 500 : 400)
.opacity(this.index === 1 ? 1 : 0.6)
Line()
.width("68vp")
.height("2vp")
.position({ x: "10vp", y: "46vp" })
.backgroundColor(this.index === 1 ? "#007DFF" : "linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
}
.id("tabTaskPool")
.height("100%")
.width("100%")
.position({ x: "4%", y: "0" })
.onClick(() => {
this.index = 1;
this.controller.changeIndex(this.index);
})
}
build() {
Row() {
Column() {
Text("ConcurrentModule")
.width("100%")
.height("41vp")
.position({ x: "7%", y: "31vp" })
.fontColor("#182431")
.fontSize("30fp")
.fontFamily("HarmonyHeiTi-Bold")
.lineHeight(41)
.fontWeight(700)
.textAlign(TextAlign.Start)
Tabs({
barPosition: BarPosition.Start,
controller: this.controller
}) {
TabContent() {
WorkerTab();
}
.width("100%")
.height("100%")
.tabBar(this.tabJsWorker)
TabContent() {
TaskPoolTab();
}
.width("100%")
.height("100%")
.tabBar(this.tabTaskPool)
}
.width("100%")
.height("696vp")
.barWidth("100%")
.barHeight("56vp")
.position({ x: "0vp", y: "80vp" })
.padding({ bottom: "24vp" })
.backgroundImage("linear-gradient(269deg,rgba(0,0,0,0.00)%,#FFFFFF 10%)")
.barMode(BarMode.Fixed)
.onChange((index: number) => {
this.index = index;
})
}
.backgroundColor("#f1f3f5")
.width("100%")
.height("100%")
}
.width("100%")
.height("100%")
}
}
-
立即执行:通过调用executeImmediately()创建一个task任务,这个任务是立即执行字符串排序。
-
超时3s执行:通过调用executeDelay()创建一个task任务,这个任务是延迟3s后执行字符串排序。
-
函数任务:调用executeFunc()接口,不创建task任务,直接调用taskpool.execute()执行字符串排序。
-
取消任务:调用cancelTask()接口,取消最后一个未执行的task任务。
-
清除:把字符串输入框和结果都清除。
-
批量拷贝文件的功能封装在MyWorker,源码参考:[MyWorker.ets]
/*
* Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { worker } from '@kit.ArkTS';
import { fileIo } from '@kit.CoreFileKit';
import { Logger, sleep } from '../common/Common';
const CONTENT = 'hello world';
const TAG: string = '[ConcurrentModule].[MyWorker]';
const FILE_NUM: number = 200;
const FILE_NUMBER: number = 9;
const LIST_FILE_TWO: number = 2;
const SLEEP_TIME: number = 100;
let workerInstance: worker.ThreadWorker | null = null;
let fileFlag: boolean = false;
export default class MyFile {
public baseDir: string = '';
public filesCount: number = 0;
private flag: boolean = false;
public realFileNames: Array<string> = [];
constructor() {
this.baseDir = AppStorage.get('sanBoxFileDir') as string;
}
readyFileToFileFs(): void {
let fileFsDir = this.baseDir + '/fileFs';
try {
if (!fileIo.accessSync(fileFsDir)) {
fileIo.mkdirSync(fileFsDir);
}
Logger.info(TAG, 'readyFileToFileFs successful');
} catch (e) {
Logger.error(TAG, `readyFileToFileFs has failed for: {message: ${(e as Error).message}, ${e}}`);
}
}
// worker file
readyFilesToWorker(): void {
let content = CONTENT + CONTENT + new Date() + '\n';
let workerDir = this.baseDir + '/workerDir';
try {
if (!fileIo.accessSync(workerDir)) {
fileIo.mkdirSync(workerDir);
}
Logger.info(TAG, 'readyFilesToWorker dpath = ' + workerDir);
for (let i = 0; i < FILE_NUM; i++) {
let myFile = '';
if (i < FILE_NUMBER) {
myFile = workerDir + `/TestFile0${i + 1}.txt`;
} else {
myFile = workerDir + `/TestFile${i + 1}.txt`;
}
Logger.info(TAG, 'readyFilesToWorker myFile = ' + myFile);
let file = fileIo.openSync(myFile, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
fileIo.writeSync(file.fd, content);
fileIo.closeSync(file);
}
Logger.info(TAG, 'readyFilesToWorker successful');
} catch (e) {
Logger.error(TAG, `readyFilesToWorker has failed for: {message: ${(e as Error).message}, ${e}}`);
}
}
async workToCopyFiles(files: Array<string>, filePath: string): Promise<void> {
try {
Logger.info(TAG, 'WorkCreator start to create worker');
let destPath = filePath;
Logger.info(TAG, 'Workerets destPath ' + destPath);
if (!fileIo.accessSync(destPath)) {
fileIo.mkdirSync(destPath);
}
if (fileIo.accessSync(destPath)) {
fileIo.listFile(destPath).then((filenames) => {
Logger.info(TAG, 'listFile succeed');
for (let i = 0; i < filenames.length; i++) {
Logger.info(TAG, 'Workerets fileName: ' + filenames[i]);
}
}).catch((err: Error) => {
Logger.info(TAG, 'list file failed with error message: ' + err.message + ', error: ' + err);
});
}
if (files !== null) {
this.realFileNames.length = 0;
for (let i = 0; i < files.length; i++) {
if (files[i] === 'deletedTag') {
continue;
}
this.realFileNames.push(files[i]);
}
}
let count = this.realFileNames.length;
for (let j = 0; j < count; j++) {
Logger.info(TAG, 'workToCopyFiles this.realFileNames = ' + this.realFileNames[j]);
}
workerInstance = new worker.ThreadWorker('entry/ets/model/WorkerCopy.ts');
if (this.realFileNames !== null) {
let srcPath = this.baseDir + '/workerDir';
workerInstance.postMessage({
srcDir: srcPath,
destDir: destPath,
fileNames: this.realFileNames
});
}
workerInstance.onexit = (code): void => {
Logger.info(TAG, `workerInstance::onexit has been exit ${code}`);
};
workerInstance.onerror = (err): void => {
Logger.info(TAG, `workerInstance::onerror has errors: ${JSON.stringify(err)}`);
};
workerInstance.onmessageerror = (event): void => {
Logger.info(TAG, `workerInstance::onmessageerror has errors: ${JSON.stringify(event)}`);
};
workerInstance.onmessage = (message): void => {
Logger.info(TAG, `workerInstance::onmessage receive data: ${JSON.stringify(message.data)}`);
if (message.data.hasOwnProperty('count')) {
Logger.info(TAG, `workerInstance::onmessage receive data length = ${message.data.count}`);
this.filesCount = message.data.count;
fileFlag = message.data.strFlag;
this.flag = true;
let fileName1: string = '';
let fileName2: string = '';
for (let i = 0; i < message.data.listFileNames.length; i++) {
Logger.info(TAG, `Worker workerInstance::onmessage receive listFileNames: ${message.data.listFileNames[i]}`);
}
if (message.data.listFileNames[0] !== undefined && message.data.listFileNames[1] !== undefined && message.data.listFileNames[LIST_FILE_TWO] === undefined) {
fileName1 = message.data.listFileNames[0] + '、';
fileName2 = message.data.listFileNames[1];
} else if (message.data.listFileNames[0] !== undefined && message.data.listFileNames[1] === undefined) {
fileName1 = message.data.listFileNames[0];
fileName2 = '';
} else {
fileName1 = message.data.listFileNames[0] + '、';
let copyFileLog: string = AppStorage.get('copyFileLog5') as string;
fileName2 = message.data.listFileNames[1] + copyFileLog;
}
AppStorage.setOrCreate('fileListName1', fileName1);
AppStorage.setOrCreate('fileListName2', fileName2);
let copyFileLog3: string = AppStorage.get('copyFileLog3') as string;
let copyFileLog4: string = AppStorage.get('copyFileLog4') as string;
let copyFileLog = '2、' + fileName1 + fileName2 + copyFileLog3 + 'copy' + copyFileLog4;
if (fileName1 !== 'undefined、') {
AppStorage.setOrCreate('copyFileShowLog', copyFileLog);
} else {
AppStorage.setOrCreate('copyFileShowLog', $r('app.string.workerLogChooseFile'));
}
Logger.info(TAG, `Worker workerInstance::onmessage receive count: ${JSON.stringify(this.filesCount)}`);
}
if (this.filesCount !== 0) {
AppStorage.setOrCreate('fileNumber', JSON.stringify(this.filesCount));
} else {
AppStorage.setOrCreate('fileNumber', '0');
AppStorage.setOrCreate('fileListName1', '');
AppStorage.setOrCreate('fileListName2', '');
}
Logger.info(TAG, 'workerInstance::onmessage Finish to process data from WorkerCopy.ts');
if (workerInstance !== null) {
workerInstance.terminate();
}
};
while (!fileFlag) {
await sleep(SLEEP_TIME);
}
} catch (e) {
Logger.error(TAG, `Worker WorkCreator error package: message: ${(e as Error).message}, ${e}`);
}
}
deleteCopyFile(filePath: string): void {
Logger.info(TAG, 'deleteCopyFile destCopyFilePath = ' + filePath);
try {
if (fileIo.accessSync(filePath)) {
let isDirectory = fileIo.statSync(filePath).isDirectory();
if (isDirectory) {
fileIo.rmdirSync(filePath);
fileIo.mkdirSync(filePath);
}
}
} catch (e) {
Logger.error(TAG, `delete workerCopyFile error package: message: ${(e as Error).message}, ${e}`);
}
}
}
- 拷贝文件:在[CopyFile.ets]中调用MyWorker.WorkToCopyFiles(),在WorkToCopyFiles方法中向worker03线程发消息,并在worker03线程中批量拷贝,拷贝完成后将结果返回。
以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下:
内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!
鸿蒙【北向应用开发+南向系统层开发】文档
鸿蒙【基础+实战项目】视频
鸿蒙面经
为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!