Harmony Next - 多线程技术 TaskPool

前言

当我们的项目日益复杂的时候,我们不可避免的会用到多线程技术,通过在子线程执行耗时任务,不阻塞主线程,从而提升用户体验。比如网络请求、复杂的本地耗时操作等等。

在鸿蒙操作系统中,有 TaskPoolWorker 这两种技术可以实现多线程编程需求。两种技术都是基于 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 函数的四秒之后,也就是需要将任务一二三都执行我那之后才返回的结果。

相关推荐
SuperHeroWu731 分钟前
【HarmonyOS】键盘遮挡输入框UI布局处理
华为·harmonyos·压缩·keyboard·键盘遮挡·抬起
sanzk5 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna9 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
ClkLog-开源埋点用户分析10 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg66810 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos
lqj_本人11 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人11 小时前
使用 Flutter 绘制一个棋盘
harmonyos
lqj_本人14 小时前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
青瓷看世界15 小时前
华为HarmonyOS打造开放、合规的广告生态 - 插屏广告
华为·harmonyos·广告投放
青瓷看世界16 小时前
华为HarmonyOS借助AR引擎帮助应用实现虚拟与现实交互的能力2-管理AR会话
华为·ar·harmonyos·虚拟现实