Harmonyos多线程之Worker基本使用

Worker主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与宿主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞宿主线程的运行。具体接口信息及使用方法详情请见Worker

Worker的注意事项

  • Worker创建后需要手动管理生命周期,且最多同时运行的Worker子线程数量为64个。
    • Worker的创建和销毁耗费性能,建议开发者合理管理已创建的Worker并重复使用。Worker空闲时也会一直运行,因此当不需要Worker时,可以调用terminate()接口或close()方法主动销毁Worker。若Worker处于已销毁或正在销毁等非运行状态时,调用其功能接口,会抛出相应的错误。
    • Worker的数量由内存管理策略决定,设定的内存阈值为1.5GB和设备物理内存的60%中的较小者。在内存允许的情况下,系统最多可以同时运行64个Worker。如果尝试创建的Worker数量超出这一上限,系统将抛出错误:“Worker initialization failure, the number of workers exceeds the maximum.”。实际运行的Worker数量会根据当前内存使用情况动态调整。一旦所有Worker和主线程的累积内存占用超过了设定的阈值,系统将触发内存溢出(OOM)错误,导致应用程序崩溃。
  • 由于不同线程中上下文对象是不同的,因此Worker线程只能使用线程安全的库,例如UI相关的非线程安全库不能使用。
  • 序列化传输的数据量大小限制为16MB
  • 使用Worker模块时,需要在宿主线程中注册onerror接口,否则当Worker线程出现异常时会发生jscrash问题。
  • 不支持跨HAP使用Worker线程文件
  • 不支持在Worker工作线程中使用AppStorage

创建Worker的注意事项

Worker线程文件需要放在"{moduleName}/src/main/ets/"目录层级之下,否则不会被打包到应用中。有手动和自动两种创建Worker线程目录及文件的方式。

手动创建Worker线程

开发者需要手动创建相关目录及文件, 此时需要配置build-profile.json5的相关字段信息,Worker线程文件才能确保被打包到应用中

在当前使用Worker线程的模块下面的对应文件目录中创建一个worker线程文件, 并增加相关配置项
在这里插入图片描述

手动创建Worker需要实现代码:

import worker, { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope } from '@ohos.worker'
import { JSON } from '@kit.ArkTS'

// 创建woker线程中和宿主线程通信的对象
const workerPort: ThreadWorkerGlobalScope = worker.workerPort

//woker线程接受主线程的消息
workerPort.onmessage = (e: MessageEvents) => {
  // 现成通信的具体逻辑
  console.log("onmessage=22222================" + JSON.stringify(e.data))

  // woker线程像宿主线程发送消息
  workerPort.postMessage("woker线程发送消息")
}

// 回调函数。表示当Worker对象接收到一条无法被序列化的消息时被调用的事件处理程序,
// 处理程序在宿主线程中执行。其中回调函数中event类型为MessageEvents,表示收到的Worker消息数据。
workerPort.onmessageerror = (ev: MessageEvents) => {
  console.log('onmessageerror=================' + JSON.stringify(ev.data))
}

// worker线程发生error错误的回调
workerPort.onerror = (err: ErrorEvent) => {
  console.log("worker.ets onerror" + err.message);
}

自动创建Worker现成

DevEco Studio支持一键生成Worker,在对应的{moduleName}目录下任意位置,点击鼠标右键 > New > Worker,即可自动生成Worker的模板文件及配置信息,无需再手动在build-profile.json5中进行相关配置。
在这里插入图片描述
当我们使用工具进行自动创建时, 我们可以看到配置文件中自动增加了,工具创建的AutoWorker线程类:
在这里插入图片描述

使用自动创建的方式,系统自动实现的代码:

import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';

const workerPort: ThreadWorkerGlobalScope = worker.workerPort;

/**
 * Defines the event handler to be called when the worker thread receives a message sent by the host thread.
 * The event handler is executed in the worker thread.
 *
 * @param e message data
 */
workerPort.onmessage = (e: MessageEvents) => {
}

/**
 * Defines the event handler to be called when the worker receives a message that cannot be deserialized.
 * The event handler is executed in the worker thread.
 *
 * @param e message data
 */
workerPort.onmessageerror = (e: MessageEvents) => {
}

/**
 * Defines the event handler to be called when an exception occurs during worker execution.
 * The event handler is executed in the worker thread.
 *
 * @param e error message
 */
workerPort.onerror = (e: ErrorEvent) => {
}

跨har包加载Worker

跨har的worker线程:
在这里插入图片描述

在entry模块中使用workerhar模块中创建的worker线程:

//宿主线程中创建woker线程
          const workerInstance = new worker.ThreadWorker("@workerhar/ets/workers/Worker.ets")

          //宿主线程像woker线程发送消息
          workerInstance.postMessage("宿主线程像夸har包的woker线程传递消息========")

          //宿主线程接受woker线程信息
          workerInstance.onmessage = (e: MessageEvents) => {
            console.log('onmessage=接受到夸har包的worker线程消息====================' + JSON.stringify(e.data))


            //销毁Worker对象
            workerInstance.terminate()
          }

          // 在调用terminate后,执行onexit
          workerInstance.onexit = (code) => {
            console.log("main thread terminate");
          }

          workerInstance.onerror = (err: ErrorEvent) => {
            console.log("main error message " + err.message);
          }

输出结果:
在这里插入图片描述

注意: 主要区别是在与worker文件的导入路径

多级Worker的声明周期管理

由于支持创建多级Worker(即通过父Worker创建子Worker的机制形成层级线程关系),且Worker线程生命周期由用户自行管理,因此需要注意多级Worker生命周期的正确管理。若用户销毁父Worker时未能结束其子Worker的运行,会产生不可预期的结果。建议用户确保子Worker的生命周期始终在父Worker生命周期范围内,并在销毁父Worker前先销毁所有子Worker。

示例:

主线程核心代码:

// 在主线程创建worker线程(父worker),在worker线程中在次创建worker线程(子线程)
        const parentworker = new worker.ThreadWorker('../workers//ParentWorker')

        parentworker.onmessage = (e: MessageEvents) => {
          console.log('主线程收到父worker线程信息========================' + e.data)
        }

        parentworker.onexit = () => {
          console.log("父worker退出=============================")
        }

        parentworker.onerror = (error: ErrorEvent) => {
          console.log('主线程收到父worker的报错=======================' + error)
        }

        parentworker.postMessage('主线程发送消息给父worker')
      })

parentworker.ets的核心代码

workerPort.onmessage = (e: MessageEvents) => {
  if (e.data === "主线程发送消息给父worker") {
    // 创建一个子worker
    let childworker = new worker.ThreadWorker("../workers/ChilderWorker");

    childworker.onmessage = (e: MessageEvents) => {
      console.log("父Worker收到子Worker的信息================" + e.data);

      if (e.data === '子Worker向父Worker发送信息') {
        workerPort.postMessage("父Worker向主线程发送信息");
      }
    }

    childworker.onexit = () => {
      console.log('子Worker退出=================')
      workerPort.close()
    }

    childworker.onerror = (err: ErrorEvent) => {
      console.log("子Worker发生报错 " + err);
    }

    childworker.postMessage("父Worker向子Worker发送信息");
  }
}

childerworker.ets核心代码:

workerPort.onmessage = (e: MessageEvents) => {
  if (e.data === "主线程发送消息给父worker") {
    // 创建一个子worker
    let childworker = new worker.ThreadWorker("../workers/ChilderWorker");

    childworker.onmessage = (e: MessageEvents) => {
      console.log("父Worker收到子Worker的信息================" + e.data);

      if (e.data === '子Worker向父Worker发送信息') {
        workerPort.postMessage("父Worker向主线程发送信息");
      }
    }

    childworker.onexit = () => {
      console.log('子Worker退出=================')
      workerPort.close()
    }

    childworker.onerror = (err: ErrorEvent) => {
      console.log("子Worker发生报错 " + err);
    }

    childworker.postMessage("父Worker向子Worker发送信息");
  }
}

Worker和宿主线程的通信

worker的代码是上面的手动创建worker示例的代码.

示例代码:

//宿主线程中创建woker线程
          const workerInstance = new worker.ThreadWorker("../manualcreate/manualWorker")

          //宿主线程像woker线程发送消息
          workerInstance.postMessage("宿主线程像woker线程传递消息========")

          //宿主线程接受woker线程信息
          workerInstance.onmessage = (e: MessageEvents) => {
            console.log('onmessage=1111====================' + JSON.stringify(e.data))


            //销毁Worker对象
            workerInstance.terminate()
          }

          // 在调用terminate后,执行onexit
          workerInstance.onexit = (code) => {
            console.log("main thread terminate");
          }

          workerInstance.onerror = (err: ErrorEvent) => {
            console.log("main error message " + err.message);
          }

输出结果:
在这里插入图片描述

<think>嗯,用户想知道在ArkTS中如何实现多线程处理,还要求示例。首先我得回忆一下HarmonyOS Next中ArkTS的多线程机制。记得有TaskPool和Worker两种方式,对吧?根据之前的引用资料,TaskPool适合短时间任务,不超过3分钟的那种,而Worker适合长时间运行的任务,比如超过3分钟或者需要更复杂的线程通信的情况。 先整理TaskPool的实现步骤。根据引用[2],使用TaskPool的话需要导入taskpool模块,然后用execute方法执行任务。示例代码可能包括定义一个函数,然后传给taskpool.execute。这时候要注意,传递给TaskPool的函数必须是普通函数,不能是类的方法或者使用this,否则会报错。比如用户想计算斐波那契数列,可以写个fibonacci函数,然后用execute调用,返回Promise结果。 然后是Worker部分。引用[5]提到,在pages同级目录新建Worker,生成对应的js文件。主线程里用new worker.ThreadWorker来创建实例,通过postMessage发送数据,监听onmessage接收结果。记得Worker里的代码要处理onmessage事件,执行耗时操作后postMessage返回结果。同时,Worker完成后要调用terminate或parentPort.close来释放资源,避免内存泄漏。 另外,引用[4]指出TaskPool没有主线程向子线程发消息的接口,所以如果需要双向通信,可能得用Worker。比如下载任务中暂停或继续,可能需要定义枚举命令,通过消息传递来控制子线程的行为。 还要注意,TaskPool的任务不能操作UI,而Worker也不能直接更新UI,需要通过消息通知主线程,由主线程处理UI变化。比如在Worker计算完成后,将结果发送给主线程,主线程接收到后更新页面显示。 用户可能需要具体的代码示例,所以得分别给出TaskPool和Worker的示例。比如TaskPool处理斐波那契数,Worker处理累加计算。同时要指出两者的使用场景和限制,比如执行时间限制、是否需要复杂通信等。 最后,生成相关问题的时候,可以考虑性能分析、优化方法、应用场景、跨线程通信对比、异常处理等。这些都能帮助用户更深入理解ArkTS多线程的实现和应用。</think>### ArkTS多线程实现方式及示例 在HarmonyOS Next中,ArkTS通过`TaskPool`和`Worker`两种方式实现多线程处理。以下为具体实现方法及示例: --- #### 一、TaskPool轻量级线程池 **适用场景**:短时间任务(执行时间≤3分钟),如简单计算、网络请求等。 **实现步骤**: 1. **导入模块**:`@ohos.taskpool` 2. **定义任务函数**:需为普通函数(不可用类方法或`this`) 3. **提交任务**:通过`taskpool.execute()`异步执行 4. **接收结果**:通过`Promise`获取返回值 **示例代码**: ```typescript // 主线程代码 import taskpool from '@ohos.taskpool'; // 定义任务函数(需为普通函数) function fibonacci(n: number): number { if (n <= 1) return n; return fibonacci(n-1) + fibonacci(n-2); } // 提交任务 let task = new taskpool.Task(fibonacci, 30); taskpool.execute(task).then((result) => { console.log(`Fibonacci结果:${result}`); }); ``` **注意事项**: - 任务函数不可操作UI - 参数需支持序列化(避免使用闭包) - 超时任务会被自动终止[^2] --- #### 二、Worker独立线程 **适用场景**:长时间任务(>3分钟)或需复杂跨线程通信。 **实现步骤**: 1. **创建Worker文件**:在`entry/src/main/ets`下新建`workers`目录 2. **定义线程逻辑**:处理`onmessage`事件 3. **主线程通信**:通过`postMessage`发送数据 4. **资源释放**:任务完成后调用`terminate()` **示例代码**: ```typescript // 主线程代码 import worker from '@ohos.worker'; // 创建Worker实例 const myWorker = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts'); // 发送数据 myWorker.postMessage({n1: 5, n2: 8}); // 接收结果 myWorker.onmessage = (e: MessageEvents) => { console.log(`计算结果:${e.data.he}`); myWorker.terminate(); // 释放资源 }; ``` **Worker文件(MyWorker.ts)**: ```typescript // Worker线程代码 import worker from '@ohos.worker'; // 监听主线程消息 workerPort.onmessage = (e: MessageEvents) => { let he = 0; for(let i=0; i<21000000; i++) { // 模拟耗时操作 he += e.data.n1 + e.data.n2 + i; } workerPort.postMessage({he}); // 返回结果 }; ``` **注意事项**: - Worker线程无法直接操作UI - 需显式管理线程生命周期 - 支持双向通信(主线程可向Worker发送控制指令)[^4][^5] --- #### 三、技术选型对比 | 特性 | TaskPool | Worker | |---------------|---------------------------|-----------------------| | 执行时长 | ≤3分钟 | 无限制 | | 通信方式 | 单向(任务→结果) | 双向 | | 线程管理 | 自动回收 | 手动控制 | | 适用场景 | 轻量级并行任务 | 复杂长期任务 | | UI操作 | 禁止 | 禁止 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值