Harmonyos多线程之Worker基本使用

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需要实现代码:

typescript 复制代码
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线程类:

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

typescript 复制代码
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线程:

typescript 复制代码
//宿主线程中创建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。

示例:

主线程核心代码:

typescript 复制代码
// 在主线程创建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的核心代码

typescript 复制代码
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核心代码:

typescript 复制代码
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示例的代码.

示例代码:

typescript 复制代码
//宿主线程中创建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);
          }

输出结果:

相关推荐
SoraLuna4 小时前
「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
开发语言·算法·macos·ui·华为·harmonyos
SuperHeroWu74 小时前
【HarmonyOS】HarmonyOS 和 Flutter混合开发 (一)之鸿蒙Flutter环境安装
flutter·华为·jdk·harmonyos·鸿蒙·环境安装·混合开发
特立独行的猫a4 小时前
HarmonyOS ArkTS中视频播放Video组件实现竖屏到横屏切换
华为·音视频·harmonyos
青瓷看世界5 小时前
华为HarmonyOS帮助应用实现在线认证服务 -- 2 FIDO免密身份认证
华为·harmonyos·在线认证
Swift社区5 小时前
HarmonyOS 实时监听与获取 Wi-Fi 信息
harmonyos
Freerain996 小时前
鸿蒙Next页面和自定义组件生命周期
华为·harmonyos
枫叶丹48 小时前
【HarmonyOS之旅】HarmonyOS开发基础知识(一)
华为od·华为·华为云·harmonyos
play_big_knife1 天前
鸿蒙项目云捐助第四讲鸿蒙App应用的登陆注册页实现
华为·华为云·harmonyos·鸿蒙·云开发·鸿蒙开发·鸿蒙next
AI鸿蒙1 天前
鸿蒙Next之har工程依赖库接口二次导出
前端·harmonyos