前言
当我们的项目日益复杂的时候,我们不可避免的会用到多线程技术,通过在子线程执行耗时任务,不阻塞主线程,从而提升用户体验。比如网络请求、复杂的本地耗时操作等等。
在鸿蒙操作系统中,有 TaskPool
和 Worker
这两种技术可以实现多线程编程需求。两种技术都是基于 Actor
并发模型去实现的,它们的并发模型是基于消息通信并发模型。在今天这篇文章中,我们来看一下 TaskPool 是如何使用的。
TaskPool
TaskPool 是一套多线程的运行环境,它将线程的概念抽象起来。开发者无需关心线程的生命周期或者线程之间数据竞争的问题。我们只需往队列里面添加任务即可。
运行机制示意图如下(官方示例图):
它支持以下特性:
- 延时执行任务。
- 周期执行任务。
- 执行任务组。
- 支持任务取消。
如果有 iOS 的同学,可以理解为它跟 iOS 的 GCD 是很类似的。
下面通过示例代码来看下如何使用。
不带参数的任务
typescript
import { taskpool } from '@kit.ArkTS';
@Entry
@Component
struct Index {
build() {
Column() {
Button('执行任务')
.onClick(() => {
this.run();
})
}
}
run() {
taskpool.execute(task1).then(() => {
console.log("===执行完任务1");
});
console.log("===执行完run");
}
}
@Concurrent
async function task1() {
console.log("===执行任务1");
}
首先,第一步我们要导入该框架:import { taskpool } from '@kit.ArkTS';
。接着,我们需要并发执行的函数需要使用 @Concurrent
装饰器修饰,而且函数要是全局函数,不能放在 struct 或者 class 里面。最后,我们调用 TaskPool 的 execute()
函数执行任务即可。上述代码的打印如下:
ini
===执行完run
===执行任务1
===执行完任务1
带参数的任务
typescript
@Concurrent
async function task1(num1: number, num2: number): Promise<number> {
console.log("===执行任务1");
return num1 + num2;
}
//任务执行
run() {
taskpool.execute(task1, 1, 2).then((res) => {
console.log(`===执行完任务1,任务结果${res}`);
});
console.log("===执行完run");
}
如果执行的任务有参数,在调用 execute()
函数时将参数传进去即可。
取消任务
typescript
@Concurrent
async function task1Function(): Promise<string> {
if (taskpool.Task.isCanceled()) {
return "";
}
const time: number = Date.now();
while (Date.now() - time < 2000) {
continue;
}
return "任务1执行结果"
}
// 任务执行
run() {
const task1: taskpool.Task = new taskpool.Task(task1Function);
taskpool.execute(task1).then((res) => {
console.log(`===执行完任务1,任务结果${res}`);
}).catch((e: BusinessError) => {
console.log(`taskpool: cancel error code: ${e.code}, info: ${e.message}`);
});
setTimeout(()=>{
try {
taskpool.cancel(task1);
} catch (e) {
console.log(`taskpool: cancel error code: ${e.code}, info: ${e.message}`);
}
}, 1000);
console.log("===执行完run");
}
首先在任务函数中判断,如果当前任务被取消,直接返回。任务函数中加 2s 延时是为了模拟耗时任务。
在执行时,要将任务函数包装为 taskpool.Task
类型,接着调用 TaskPool 的执行函数。
最后,延时 1s 之后将该任务取消。run
函数的执行任务代码最后会走到任务1的 catch
分支。
延时执行任务
javascript
@Concurrent
async function task1Function() {
return "任务1执行结果"
}
run() {
const task1: taskpool.Task = new taskpool.Task(task1Function);
taskpool.executeDelayed(1000, task1).then(() => {
console.log("===执行完任务1");
});
console.log("===执行完run");
}
执行延时任务非常简单,直接调用 executeDelayed
函数,传入需要延时的时间即可。控制台打印如下:
ini
10-12 14:39:00.807 36400-36400 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===执行完run
10-12 14:39:01.820 36400-36400 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===执行完任务1
从控制台输出时间可以看到,执行完 run 函数一秒之后又执行的任务1.
执行周期任务
javascript
@Concurrent
async function task1Function() {
console.log("===任务1执行结果");
}
run() {
const task1: taskpool.Task = new taskpool.Task(task1Function);
// 每隔 1s 执行一次
taskpool.executePeriodically(1000, task1);
console.log("===执行完run");
}
执行周期任务也非常简单,直接调用 executePeriodically
函数,传入周期间隔时间即可。控制台打印如下:
ini
10-12 14:45:25.220 39157-39157 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===执行完run
10-12 14:45:26.233 39157-39231 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务1执行结果
10-12 14:45:27.226 39157-39231 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务1执行结果
10-12 14:45:28.225 39157-39231 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务1执行结果
10-12 14:45:29.229 39157-39231 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务1执行结果
执行任务组
typescript
@Concurrent
async function task1Function() {
const time: number = Date.now();
while (Date.now() - time < 1000) {
continue;
}
console.log("===任务1执行结果");
}
@Concurrent
async function task2Function() {
const time: number = Date.now();
while (Date.now() - time < 2000) {
continue;
}
console.log("===任务2执行结果");
}
@Concurrent
async function task3Function() {
const time: number = Date.now();
while (Date.now() - time < 4000) {
continue;
}
console.log("===任务3执行结果");
}
run() {
const taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
taskGroup.addTask(task1Function);
taskGroup.addTask(task2Function);
taskGroup.addTask(task3Function);
taskpool.execute(taskGroup).then(() => {
console.log("===执行完任务组");
})
console.log("===执行完run");
}
任务一、任务二和任务三分别延时 1s、2s、4s执行的。在 run 函数中,我们需要把相关组的任务包装成 taskpool.TaskGroup
类型,然后调用 execute
函数执行,控制台打印如下:
ini
10-12 14:51:51.980 42047-42047 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===执行完run
10-12 14:51:52.982 42047-42120 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务1执行结果
10-12 14:51:53.982 42047-42121 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务2执行结果
10-12 14:51:55.983 42047-42115 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===任务3执行结果
10-12 14:51:55.984 42047-42047 A03D00/com.exa....dbdemo/JSAPP com.example.dbdemo I ===执行完任务组
从控制台可以看到,执行完任务组是在执行完 run 函数的四秒之后,也就是需要将任务一二三都执行我那之后才返回的结果。