鸿蒙开发 多线程

此文章内容兼容API12,使用harmony next应用开发

概念解释

1,并发


并发

2,多线程并发

  • 常见两种模型: 基于内存共享的并发模型,和基于消息通信的并发模型
  • Actor 模型基于消息通信
  • TaskPool 和 Worker 都是基于Actor 模型实现,TaskPool更是对Worker 的进一步封装

3,TaskPool 和 Worker 区别

Worker

  • 线程池创建线程个数最多是64个。超过则创建失败。
  • 使用Worker,传输序列化数据大小限制在16MB。
  • 引用HAR/HSP前,首先要配置对HAR/HSP的依赖。不支持 跨HAP使用Worker线程文件。
  • 任务执行时长上限,无限制。
  • 不支持设置任务的优先级,不支持取消任务
  • Worker需要自行封装参数,通过 onmessage 接收返回数据
  • 生命周期:开发者自行管理Worker的数量及生命周期。

TaskPool

  • TaskPool线程池的数量会根据硬件条件、任务负载等情况动态调整。
  • Promise不支持跨线程传递,不能作为concurrent function的返回值。
  • 任务执行时长上限,为3分钟(执行耗时不能超过3分钟)。
  • 支持设置任务的优先级,支持取消任务
  • TaskPool可以直接传递参数、可以直接接收返回数据;
  • 生命周期:TaskPool自行管理生命周期,无需关心任务负载高低。

4,使用场景对比

  • 由于TaskPool的工作线程会绑定系统的调度优先级,并且支持负载均衡(自动扩缩容),而Worker需要开发者自行创建,存在创建耗时以及不支持设置调度优先级,故在性能方面使用TaskPool会优于Worker,因此大多数场景推荐使用TaskPool。
  • 运行时间超过3分钟(不包含Promise和async/await异步调用的耗时,例如网络下载、文件读写等I/O任务的耗时)的任务。例如后台进行1小时的预测算法训练等CPU密集型任务,需要使用Worker。

使用详解 - TaskPool

//[函数,参数]
function execute(func: Function, ...args: Object[]): Promise<Object>;
//[任务,优先级]
function execute(task: Task, priority?: Priority): Promise<Object>;
//[任务组,优先级]
function execute(group: TaskGroup, priority?: Priority): Promise<Object[]>;

1,使用示例

//[函数,参数]
fn1 = async () => {
    // 执行任务
    const res = await taskpool.execute(func1, this.num)
    this.num = Number(res)
  }
@Concurrent
function func1(n: number) {
  return 1 + n
}
-------
//[任务,优先级]
fn1 = async () => {
    // 同时创建和执行10个任务  指定第10个任务是最高优先级
    for (let index = 1; index <= 10; index++) {
      const task = new taskpool.Task(func1, index)
      if (index !== 10) {
        taskpool.execute(task, taskpool.Priority.LOW)
      } else {
        taskpool.execute(task, taskpool.Priority.HIGH)
      }
    }
  }
------
//[任务组,优先级]
fn1 = async () => {
    // 创建任务组
    const taskGroup = new taskpool.TaskGroup()
    for (let index = 1; index <= 10; index++) {
      const task = new taskpool.Task(func1, index)
      taskGroup.addTask(task)
    }
    // 执行任务组
    const res = await taskpool.execute(taskGroup)
  }

2,注意事项

  • 函数必须使用 @Concurrent 装饰
  • 函数需要function关键字;
  • 函数需要声明在@Component外
  • 函数如果调用了外部某类方法或类实例方法,某类需要使用装饰器@Sendable标注,通过import方式导入使用
  • 在子线程直接getContext(),是拿不到主线程的上下文的,需要主线程手动传上下文
  • 子线程不要做UI渲染操作

3,线程间通信

  • 子线程中使用 sendData发送数据,主线程中使用onReceiveData接收数据
@Concurrent
function func1(n: number) {
  // 假设这里是持续发送的数据
  taskpool.Task.sendData((new Date).toLocaleString())
  return 100
}
----
fn1 = async () => {
    const task = new taskpool.Task(func1, 100)
    //   主线程监听到的数据
    task.onReceiveData((result: string) => {
      console.log("主线程监听到的数据", result)
    })
    //   直接得到func1的结果
    const res = await taskpool.execute(task)
    console.log("直接返回的结果", res)
  }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容