鸿蒙「TaskPool|Worker」多线程并发使用详解,这一篇足够!

概念介绍

鸿蒙的多线程并发TaskPoolWorker,他们具有相同内存模型,线程间隔离内存不共享。在项目中若使用到,有几个较重要的条件或特点这里简单作出列举。

CPU密集型任务,说白了是计算型耗时任务;
I/O密集型任务,说白了是读写型耗时任务;
官方文档重点介绍了这两种基于多线程并发机制处理任务类型,我们是要深度思考下的?!「很有意义」,是否已经包含且指明了我们使用「TaskPool/Worker」来解决项目问题的方案呢?

  • Worker

    • 使用Worker,创建线程个数最多是64个。超过则创建失败。
    • 使用Worker,传输序列化数据大小限制在16MB。
    • 引用HAR/HSP前,首先要配置对HAR/HSP的依赖。不支持 跨HAP使用Worker线程文件。
    • 使用Worker模块时,需要在主线程中注册onerror接口,否则当worker线程出现异常时会发生jscrash问题。
    • 任务执行时长上限,无限制。
  • TaskPool

    • TaskPool内部会动态调整线程个数,不支持设置数量。
    • TaskPool线程池的数量会根据硬件条件、任务负载等情况动态调整。
    • 任务执行时长上限,为3分钟(执行耗时不能超过3分钟)。
    • Promise不支持跨线程传递,不能作为concurrent function的返回值。

使用详解

对Worker以及TaskPool的使用详解,个人准备以一种特别的角度来详述。从我个人初始接触及学习研究的视角,针对 如何选用如何创建如何使用注意事项条件限制 多个方面,全面剖析

选用

依据限制条件,若考虑到任务执行时间已超过3分钟,传输数据不大。且需创建的线程个数仅几个这样子,考虑选用Worker;若考虑到任务执行时间较短,且会有大量的线程需要创建、销毁和复用,不想手动对线程数量的控制,可考虑选用TaskPool。如果使用条件上,都未超出两种限制条件,那么请随意。

创建|使用

Worker创建

Worker在进行创建使用时,有手动和自动两种方式,自动的较简单。手动创建Worker线程目录及文件时,还需同步进行相关配置。 「注意事项」 Worker线程文件需要放在"{moduleName}/src/main/ets/"目录层级之下,否则不会被打包到应用中。

自动操作:moduleName目录下任意位置,点击鼠标右键 > New > Worker,即可自动生成Worker的模板文件及配置信息,无需再手动在build-profile.json5中进行相关配置。

自动创建演示:假如创建Worker文件 entry/src/main/ets/workers/MyTestWorker1.ets ; 「workers是我自己建的文件目录」则同时deveco studio将在build-profile.json5文件中发现自动配置信息如下

/// build-profile.json5
{"apiType": "stageMode","buildOption": {"sourceOption": {"workers": [ // 这里就是自动生成的worker配置;换言之,手动创建Worker的话,需要在这里配置下信息'./src/main/ets/workers/MyTestWorker1.ets']}},"targets": [{"name": "default","runtimeOS": "HarmonyOS"}]
}

创建MyTestWorker1.ets文件,自动生成Worker文件模板如下

手动创建Worker文件的话,文件内容如监听方法和错误捕捉方法的监听,需要仿照模板编写。同时要在build-profile.json5文件中配置下配置信息。

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.* [当工作线程接收到主线程发送的消息时,onmessage将被触发]* The event handler is executed in the worker thread.* [onmessage将在工作线程中被执行]* @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.* [当工作线程接收到无法反序列化的消息时,onmessageerror将被触发]* The event handler is executed in the worker thread.*[onmessageerror将在工作线程中被执行]* @param e message data*/
workerPort.onmessageerror = (e: MessageEvents) => {
}/*** Defines the event handler to be called when an exception occurs during worker execution.* [在工作线程执行期间发生异常时,将会调用onerror程序]* The event handler is executed in the worker thread.*[onerror将在工作线程中被执行]* @param e error message*/
workerPort.onerror = (e: ErrorEvent) => {
}
Worker使用

Worker多线程并发,在创建工作线程文件后。在主线程中发起对工作线程调用时,传入文件地址作为入参。若传入地址不对,则报错提示:

Error message:The worker file path is invalid path, the file path is invaild, can't find the file.
Error code:
SourceCode:const workerThread: worker.ThreadWorker = new worker.ThreadWorker('entry/src/main/ets/workers/MyTestWorker1.ets');

在这里插入图片描述

「注意事项」 如何传入正确工作线程文件路径?
工作线程创建过程解释说明,

  • 先在entry/src/main/ets 目录下创建了workers文件目录,(「注意事项」 Worker线程文件需要放在"{moduleName}/src/main/ets/"目录层级之下,否则不会被打包到应用中。)
  • 然后在该workers文件目录下新建工作线程文件MyTestWorker1.ets,
  • 此时build-file.json5中自动配置信息是 "workers": ['./src/main/ets/workers/MyTestWorker1.ets' ]
  • 但不能直接使用这个配置的信息地址作为入参,而实际应该传入的入参地址,示例「可自行对比区别」: const workerThread: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/MyTestWorker1.ets');

手动编写一个简单DEMO,在UI界面onPageShow生命周期方法中创建Worker实例对象,构造方法中传入Worker线程文件的路径。实现主线程和worker工作线程之间的通信逻辑。
使用Worker实现多线程并发这一能力,首要要搞懂哪里写执行耗时任务在哪里开启且如何开启线程 这就和安卓起一个Thread线程的思路很像。

「使用要点」「注意事项」

worker.ThreadWorker实例对象是向Worker线程文件发送消息或接收Worker线程文件发送的消息。说白了就是开启线程执行的位置,如通过postMessage发送消息。即可通知worker线程文件中方法执行耗时任务。
worker.ThreadWorker就是用来开启线程工作用的。

workerThread.onmessage方法,监听并接收Worker线程文件发出的消息。比如耗时线程工作完成了,需要告知主线程此时此刻的进度
workerThread.postMessage方法,向Worker线程文件发送消息。比如通知耗时线程开始工作

/// 下面是部分主要代码,为方便阅读非重要内容已省略
/// ets/pages/HomePage.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';@Entry
@Component
struct HomePage {onPageShow(): void {// constructor(scriptURL: string, options?: WorkerOptions);// 构造方法入参Worker线程文件路径「关注」const workerThread: worker.ThreadWorker = new worker.ThreadWorker('entry/src/main/ets/workers/MyTestWorker1.ets');// 「通信」这里接收Worker线程文件中发出的消息workerThread.onmessage = ((event: MessageEvents) => {const type = event.data.type as number;if (type === 2) { // 这里是的匹配工作,是由worker工作线程通过workerPort.postMessage({type: 2, ..})发来的信息。console.error("打印日志,worker主线程收到worker工作线程发来的消息:", event.data.value)}})workerThread.onerror = ((event: ErrorEvent) => {})// 「通信」这里向Worker线程文件发送消息workerThread.postMessage({ 'type': 1, value: '「主线程数据包」' })}build() {Stack({ alignContent: Alignment.Top }) {... 省略...

「使用要点」「注意事项」

worker.workerPort实例对象是主线程发送消息或接收主线程发送的消息。worker.workerPort就是用来执行耗时任务工作用的。

workerPort.onmessage方法,监听并接收主线程发出的消息。
workerPort.postMessage方法,向主线程发送消息。

/// ets/workers/MyTestWorker1.ets
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) => {const type = e.data.type as number;// 耗时操作,逻辑处理等if (type === 1) {console.error("打印日志,worker工作线程收到主线程发来的消息:", e.data.value)workerPort.postMessage({type: 2, value: '「工作线程数据包」'}) // 通知zhuworker线程}
}
...省略...

运行上面源码执行结果
在这里插入图片描述

TaskPool创建|使用

TaskPool在创建及使用上较Worker则过度简单了,等同于拿来即用。
进入TaskPool#execute方法源码,(即从下面截图中)看到在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[]>;

针对这三种重载方法创建执行方式,没有太合适且简单易懂的示例代码做演示。就现场手动码一段正确代码演示下


import { taskpool } from '@kit.ArkTS';/**工作任务,用来执行耗时操作:CPU/IO密集型*/
// 「提示」1,需要加装饰器@Concurrent;2,需要function关键字;3,需要声明在@Component外;4,返回值须要是值类型。
@Concurrent
async function taskMethod1 (): Promise<number> {// return Promise.resolve(9) // 不支持。Promise.resolve仍是Promise,其状态是pending,无法作为返回值使用。return 1; // 返回值仅能是「携带值res引用,如const res = [1,2,3]」值类型
}
/**工作任务,用来执行耗时操作:CPU/IO密集型*/
@Concurrent
async function taskMethod2 (): Promise<string>  {return 'hello world'; // 返回值仅能是「携带值res引用,如const res = [1,2,3]」值类型
}@Entry
@Component
struct HomePage {onPageShow(): void {this.execute()}async execute() {// function execute(func: Function, ...args: Object[]): Promise<Object>;const resultMethod = await taskpool.execute(taskMethod1, taskMethod2)// function execute(task: Task, priority?: Priority): Promise<Object>;const task1 = new taskpool.Task('任务名称「非必填」', taskMethod1)const resultTask = await taskpool.execute(task1, taskpool.Priority.HIGH)// function execute(group: TaskGroup, priority?: Priority): Promise<Object[]>;const group1 = new taskpool.TaskGroup('定义任务组名称「非必填」') // TaskGroup有两个构造方法,一个无参,一个有字符串入参group1.addTask(taskMethod1) // 向任务组中添加任务方法的引用group1.addTask(taskMethod2) // 向任务组中添加任务方法的引用const resultGroup = await taskpool.execute(group1, taskpool.Priority.HIGH)console.error('打印输出:', resultMethod, resultTask, resultGroup)}build() {Stack({ alignContent: Alignment.Top }) {
...省略...

「注意事项」「提示」实现任务的函数需要①使用装饰器@Concurrent标注,且②仅支持在.ets文件中使用③方法需要function修饰
如果在@Component内部创建任务,会提示报错The @Concurrent decorator can decorate only common functions and async functions. <ArkTSCheck>,因此④需要在@Component外部创建才可以

「注意事项」「另外」如果在使用装饰器@Concurrent标注的任务方法中调用了某类的方法类.方法名(args) 或 类实例.方法名(args),①声明类须使用装饰器@Sendable标注。如果不是②通过import方式导入使用,则提示报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>

  • 未使用import引入时,错误提示截图图示

在这里插入图片描述

正确通过import方式引入并调用方法的方式,如下。


/**声明类,任务方法中将引用该类SendableTask,使用装饰器@Sendable修饰*/
@Sendable
export default class SendableTask {private static instance: SendableTask = new SendableTask();static getInstance(): SendableTask {// 获取单例return SendableTask.instance;}// 声明模拟一个方法static oneSyncMethod(): number {return 10;}oneMethod(): number {return 2;}
}

下面的截图中张贴了具体的调用逻辑,如何在任务方法@Concurrent async function taskMethod1中对使用外部类方法SendableTask.oneSyncMethod调用,
在这里插入图片描述
接下来跑下DEMO程序,执行TaskPool多线程并发代码,运行结果输出如截图所示:
在这里插入图片描述

08-14 14:43:34.406  45415-45415  A03D00/JSAPP  pid-45415   E   打印输出: 10 10 10,hello world

「总结」从截图debug显示及输出日志结果,可以发现并得出结论,taskpool.execute执行并发任务的三种重载方法,在执行时,各自输出的结果来源~如下

// 输出结果值来源:结果值为func方法执行结果。
function execute(func: Function, ...args: Object[]): Promise<Object>;// 输出结果值来源:结果值为task中调用执行的func方法执行结果。
function execute(task: Task, priority?: Priority): Promise<Object>;// 输出结果值来源:结果值是个数组,数组中每个元素值,为每个task中调用执行的func方法执行结果。
function execute(group: TaskGroup, priority?: Priority): Promise<Object[]>;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/401068.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美国消费者信心下滑与金属市场动向

消费者信心降至低点 根据密歇根大学的消费者信心指数&#xff0c;美国7月份的消费者信心跌至8个月来的最低水平。尽管技术上美国并未陷入经济衰退&#xff0c;但Affirm调查显示&#xff0c;大约60%的美国人认为经济处于衰退状态。Gallup的调查也发现&#xff0c;三分之一的美国…

《Ubuntu22.04环境下的ROS2学习笔记2》

一、在ROS2环境下创建功能包 如果您已经完成了上一小节的内容&#xff0c;那么接下来您一定渴望自己创建一个功能包来实现相应的功能。在ROS1中&#xff0c;您创建的功能包可以既写C/C&#xff0c;又写python&#xff0c;但ROS2中不允许用户这么做&#xff0c;您的C/C和python代…

uniapp 中 web-view 向 App 传递消息

web-view向App传递消息 引入官方库 在web项目中引入官方库 uni.webview.1.5.4.js &#xff0c;可以从uniapp官方示例库中下载&#xff0c;下载后放入web项目目录下即可&#xff0c;本文放在js文件夹中&#xff0c;然后在web项目页面中引入。 官网对于uni-app使用web-view的介…

WebDeveloper:1靶机

端口扫描 靶机ip地址为192.168.153.158 目录扫描 访问80端口 拼接访问 /ipdata 发现了一个流量包 放在 wireshark 查看&#xff0c;找到 账号密码 账号&#xff1a;webdeveloper 密码&#xff1a;Te5eQg&4sBS!Yr$)wf%(DcAd 拼接 /wp-login.php 找到登录框 登录成功 找…

安卓TV入门项目

android studio创建tv项目 下载android studio点此下载 配置环境变量&#xff1a; d盘新增Android文件夹&#xff0c;创建android-avd和android-sdk文件夹 环境变量名称&#xff1a;ANDROID_HOME 环境变量值&#xff1a;D:\Android\android-sdk 环境变量名称&#xff1a;ANDRO…

海外媒体发稿:法新社发稿7种吸引住读者的文章标题

写一篇科谱详细介绍文章内容在现代的媒体环境中&#xff0c;吸引住读者成为了一个重要的考验。彭博社做为国际知名的财经资讯组织&#xff0c;经过多年实践活动总结出七种吸引住读者的差异表达形式。本文将对这七种形式进行科谱详细介绍&#xff0c;可以帮助读者更好地了解并应…

星地多网融合调度平台:高效融合,智慧救援

在应急救援领域&#xff0c;通信的畅通无阻是保障救援行动成功的关键。然而&#xff0c;面对复杂多变的救援环境和多样化的通信需求&#xff0c;传统的通信系统往往难以满足现代应急救援的高标准要求。为了克服这些挑战&#xff0c;星地多网融合调度平台应运而生&#xff0c;它…

MySQL-进阶篇-索引

文章目录 1. 准备工作2. 索引概述2.1 什么是索引2.2 索引的优缺点 3. 索引的结构3.1 索引结构介绍3.2 二叉树3.3 BTree3.4 BTree3.5 MySQL 中的 BTree3.6 Hash3.7 思考题&#xff1a;为什么 InnoDB 存储引擎选择使用 BTree 索引结构 4. 索引的分类5. 索引的语法5.1 创建索引5.2…

数字营销中的人工智能 --- 完整指南 (By Hubspot)

原文作者&#xff1a;Rebecca Riserbato 原文发布日期&#xff1a;2024年3月7日 翻译和编辑&#xff1a;数字化营销工兵 【引言】 ChatGPT和谷歌巴德已经加入聊天。如果你还没有加入数字营销中关于人工智能&#xff08;AI&#xff09;的对话&#xff0c;你就错过了这个营销…

ElasticSearch数据建模

文章目录 如何处理关联关系避免过多字段避免正则/通配符/前缀查询避免空值引起的聚合不准为索引的Mapping加入Meta 信息 如何处理关联关系 Object: 优先考虑反范式&#xff08;Denormalization&#xff09; Nested: 当数据包含多数值对象&#xff0c;同时有查询需求 Child/Pa…

宝塔面板如何修改域名和网站名

目录 前言修改域名修改网站名 前言 BT宝塔面板是一款安全高效的服务器运维平台&#xff0c;windows和Linux系统都可以使用&#xff0c;安装也简单&#xff0c;相信很多开发者都在用它。 但当我们创建的网站需要更换新的域名&#xff0c;面板中的网站名官方却没有给修改的地方&…

DETR论文,基于transformer的目标检测网络 DETR:End-to-End Object Detection with Transformers

transformer的基本结构: encoder-decoder的基本流程为&#xff1a; 1&#xff09;对于输入&#xff0c;首先进行embedding操作&#xff0c;即将输入映射为向量的形式&#xff0c;包含两部分操作&#xff0c;第一部分是input embedding&#xff1a;例如&#xff0c;在NLP领域&…

动作捕捉与数字人实时交互实训室解决方案:赋能数字人微专业实践课程

随着近年来虚拟现实技术产业与元宇宙数字人产业不断发展&#xff0c;就业市场对元宇宙、影视动画、游戏、艺术创作、舞台特效、虚拟数字人、虚拟主播等行业具备相关技能人才的需求增加&#xff0c;面向数字媒体艺术设计、影视多媒体技术、广告设计与制作、电子商务、广播电视新…

【论文阅读】BoT-SORT: Robust Associations Multi-Pedestrian Tracking

题目&#xff1a;BoT-SORT: Robust Associations Multi-Pedestrian Tracking 作者&#xff1a;Nir Aharon* Roy Orfaig Ben-Zion Bobrovsky motivation: 作者来得很直接&#xff0c;就说他们用相机运动模型和优化卡尔曼做了个可以解决具有挑战的跟踪问题的算法:BOT-SORT;说他们…

Ubuntu22.04安卓编译环境搭建及so库编译

1.配置Android-ARM64开发环境工具链: vim ~/.profile 或者 ~/.bashrc 或者 /etc/profile 编辑环境变量文件 输入下面内容 export CROSS_TRIPLE=aarch64-linux-android export CROSS_ROOT=/usr/${CROSS_TRIPLE} export ANDROID_NDK=${CROSS_ROOT} export AS=${CROSS_ROOT}…

【人工智能】数据集合集!

本文将为您介绍10个经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 点击蓝字 关注我们 1 Habitat Platform 发布方&#xff1a; Facebook AI Research西蒙菲莎大学佐治亚理工学院Facebook Reality LabsIntel LabsUniversity of California, Berkeley…

20条项目经理成功经验,助你玩转项目管理软件

项目管理软件是现代项目管理不可或缺的工具&#xff0c;但仅仅掌握软件操作并不足以让你成为一名成功的项目经理。你需要的是经验和技巧&#xff0c;将软件的功能与实际项目管理相结合&#xff0c;才能真正发挥其优势。本文将分享20条项目经理成功经验&#xff0c;助你玩转项目…

基于华为atlas的皮带跑偏、空载、堆煤、启停探索

生乎吾前&#xff0c;其闻道也固先乎吾&#xff0c;吾从而师之&#xff1b;生乎吾后&#xff0c;其闻道也亦先乎吾&#xff0c;吾从而师之。吾师道也&#xff0c;夫庸知其年之先后生于吾乎&#xff1f;是故无贵无贱&#xff0c;无长无少&#xff0c;道之所存&#xff0c;师之所…

[C#]实现GRPC通讯的服务端和客户端实例

最近要做两个软件之间消息的通讯&#xff0c;学习了一下GRPC框架的通讯。根据官方资料做了一个实例。 官方资料请参考&#xff1a;Create a .NET Core gRPC client and server in ASP.NET Core | Microsoft Learn 开发平台&#xff1a;Visual Studio 2022 开发前提条件&#x…

图像识别,图片线条检测

import cv2 import numpy as np # 读取图片 img cv2.imread(1.png)# 灰度化 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 边缘检测 edges cv2.Canny(gray, 100, 200) 当某个像素点的梯度强度低于 threshold1 时&#xff0c;该像素点被认为是非边缘&#xff1b;当梯度强度…