初学者的鸿蒙多线程并发之 TaskPool 踩坑之旅

1. 背景

  • 目标群体:鸿蒙初学者

  • 版本:HarmonyOS 3.1/4.0

  • 背景:鸿蒙 App 的全局路由管理功能,需要在 App 启动时初始化对 raw 下的相关配置文件进行读取、解析并缓存。App 启动时涉及到了大量模块的初始化,好多模块都涉及到 IO 以及计算操作。鸿蒙的 ArkTS 是在继承 TypeScript 语法的基础上进行了优化,但是其脱离不了 js,js 又是单线程的,故担忧其性能。果断查阅官方文档描述,不出所料官方是这么回复的:

    ArkTS 层接口的异步如果不涉及 I/O 操作,则异步任务会在主线程的微任务执行时机触发,仍然占用主线程。推荐使用 TaskPool,分发到后台任务池进行。

就是这个回复,让我这个初学者开启了多线程异步任务的踩坑之旅。

2. 并发

2.1 概述

并发是指在同一时间段内,能够处理多个任务的能力。HarmonyOS 系统提供了异步并发和多线程并发两种处理策略。

  • 异步并发是指异步代码在执行到一定程度后会被暂停,以便在未来某个时间点继续执行,这种情况下,同一时间只有一段代码在执行。

  • 多线程并发允许在同一时间段内同时执行多段代码。在主线程继续响应用户操作和更新 UI 的同时,后台也能执行耗时操作,从而避免应用出现卡顿。

ArkTS 支持异步并发和多线程并发。

  • Promise 和 async/await 提供异步并发能力,适用于单次 I/O 任务的开发场景。详细请参见异步并发概述。(这个就是 js 的异步任务,官方文档资料中也指出其适用于单次 I/O 的场景开发,例如一次网络请求、一次文件读写等操作。)

  • TaskPool 和 Worker 提供多线程并发能力,适用于 CPU 密集型任务、I/O 密集型任务和同步任务等并发场景。详细请参见多线程并发概述。(我们需要的就是这个,App 启动过程中涉及大量 IO、计算等操作)。当任务不需要长时间(3 分钟)占据后台线程,而是一个个独立的任务时,推荐使用 TaskPool。

2.2 多线程并发之 TaskPool

并发模型是用来实现不同应用场景中并发任务的编程模型,常见的并发模型分为基于内存共享的并发模型和基于消息通信的并发模型。

Actor 并发模型作为基于消息通信并发模型的典型代表,不需要开发者去面对锁带来的一系列复杂偶发的问题,同时并发度也相对较高,因此得到了广泛的支持和使用。

当前 ArkTS 提供了 TaskPool 和 Worker 两种并发能力,TaskPool 和 Worker 都基于 Actor 并发模型实现。

PS:TaskPool 会随着应用进程起一个线程,省去了首次任务执行创建线程的开销,线程创建开销较小。

鸿蒙的多线程并发都是基于 Actor 并发模型实现,不是基于内存共享的。你不需要考虑对内存上锁导致的一系列功能、性能问题。但是 Actor 并发模型每一个线程都是一个独立 Actor,每个 Actor 有自己独立的内存,Actor 之间通过消息传递机制触发对方 Actor 的行为,不同 Actor 之间不能直接访问对方的内存空间。Actor 并发模型线程之间是内存隔离的。

3.TaskPool 开发流程(踩坑之旅)

好的,看完文档我就开始按照官方流程进行了如下代码编写:

1. 使用@Concurrent 注解定义并发函数,在函数中执行 IO、计算等耗时操作。

//并发函数
@Concurrent
async function loadDnsTable(): Promise<Map<string, string>> {//TODO:读取raw下的资源文件,对齐进行解析并且缓存
}

2. Harmony 要求并发函数必须是全局 function 不能是类方法,???那我如何调用我自己创建的 RawTableReader 类的方法去读取、解析并且缓存路由表。故再次翻阅官方文档,终于在 FAQ 中找到了答案:如何将类 Java 语言的线程模型(内存共享)的实现方式转换成在 ArkTS 的线程模型下(内存隔离)的实现方式话说你们就不能将他写在 taskpool 文档里么?!

export interface RawTableReader extends lang.ISendable {readRawTable(context: common.Context): Map<string, string>;
}

并发函数修改后如下:

@Concurrent
function loadDnsTable(args: Object[]): Map<string, string> {let rawTableReader: RawTableReader = args[0] as RawTableReader;//此处的context类是EntryAbility启动时后注入到VirtualDomain单例类中的let context: common.Context = VirtualDomain.getInstance().getAppContext();return rawTableReader.readRawTable(context);
}

3. 使用 TaskPool 执行包含密集 I/O 的并发函数:通过调用 execute()方法执行任务,并在回调中进行调度结果处理。

let task: taskpool.Task = new taskpool.Task('vdn', loadDnsTable, rawTableReader);
taskpool.execute(task).then((result: Object) => {let r: Map<string, string> = result as Map<string, string>;}).catch((error: BusinessError) => {VdnLog.warn(`loadDnsTable error code = ${error.code} message = ${error.message}`);
});

4. 好了开发完了,我开始了我的一次运行。不出意外报错了,断点调试半天大致意思是:context is undefined

  1. 难道单例类没初始化注入 context?检查代码以及断点再次尝试,EntryAbility 启动时已经注入了全局 context。

  2. 好吧,那我直接在 context 获取地方进行断点。又是小半天过去,我发现了问题两次调用 VirtualDomain.getInstance()返回的实例竟然不是一个?! ! .

  3. 我又思考并且到处翻阅文档好久,总算想起来了 Harmony 的多线程是基于 Actor 的内存隔离的不是内存共享的,我在主线程注入的 context 的 VirtualDomain 单例对象跟我子线程获取到的根本就不是一个,那肯定就 undefined 了。

5. 我想起来之前华为的官方人员在 FAQ 中回复可以使用应用全局状态存储 AppStorage 缓存 context 对象,于是我继续修改代码 context 改为使用官方全局单例 AppStorage 进行存储获取。结果是:再次失败,好了我用实践证明了官方的 AppStorage 在多线程情况下也是有问题的。大家使用时一定注意!

6. 我就不信一个 context 我就解决不了了?再次查阅官方文档皇天不负苦心人,我再次找到了 TaskPool 和 Worker 支持的序列化类型这篇文档里描述了 context 是 Native 绑定对象可以在 TaskPool 中进行序列化传输。因此再次修改代码

export calss xxx {...let context: common.Context = VirtualDomain.getInstance().getAppContext();let task: taskpool.Task = new taskpool.Task('vdn', loadDnsTable, rawTableReader, context);...
}
@Concurrent
function loadDnsTable(args: Object[]): Map<string, string> {let rawTableReader: RawTableReader = args[0] as RawTableReader;let context: common.Context = args[1] as common.Context;return rawTableReader.readRawTable(context);
}

7.这次代码直接报错了 Casting "Non-sendable" data to "Sendable" type is not allowed (arkts-sendable-as-expr) <ArkTSCheck>

我按照你的官方 task api 构建的 task,你也说了 context 是 Native 绑定对象是支持的序列化类型。结果 let context: common.Context = args[1] as common.Context; 你直接给我编译报错?

8. 继续思考,进行了如下修改,编译 OK,运行测试也👌🏻,我的踩坑之旅总算结束了😭。

//虽然入参是Object[]对象,这里的args要注意必须使用lang.ISendable[],否则就会编译报错
@Concurrent
function loadDnsTable(args: lang.ISendable[]): Map<string, string> {let rawTableReader: RawTableReader = args[0] as RawTableReader;let context: common.Context = args[1] as common.Context;return rawTableReader.readRawTable(context);
}

4. 总结

4.1 技术经验

  • Harmony 单次 IO 可以使用异步任务,如果涉及到多次 IO 或者大量计算建议使用多线程异步并发,异步任务的微任务也会有一定程度卡顿。

  • Harmony 的多线程是基于 Actor 内存隔离的,单例是失效的,如果需要使用相关成员变量或者方法请进行序列化传输

  • Harmony 官方的全局状态存储 AppStorage 在多线程情况下也是失效的

  • Harmony 的 TaskPool 会随着应用进程起一个线程,省去了首次任务执行创建线程的开销,线程创建开销较小

  • 如果是时长大于 3 分钟的耗时任务,需要使用 Worker

4.2 后续

  • taskpool 还支持组任务、取消任务、依赖任务。

  • worker 的用法

  • 未来 Harmony 正式版推出之后是否会推出 App 进程下线程可以共享数据的模块

5. 团队介绍

三翼鸟数字化技术平台-智家APP平台」通过持续迭代演进移动端一站式接入平台为三翼鸟APP、智家APP等多个APP提供基础运行框架、系统通用能力API、日志、网络访问、页面路由、动态化框架、UI组件库等移动端开发通用基础设施;通过Z·ONE平台为三翼鸟子领域提供项目管理和技术实践支撑能力,完成从代码托管、CI/CD系统、业务发布、线上实时监控等Devops与工程效能基础设施搭建。

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

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

相关文章

巨潮股票爬虫逆向

目标网站 aHR0cDovL3dlYmFwaS5jbmluZm8uY29tLmNuLyMvSVBPTGlzdD9tYXJrZXQ9c3o 一、抓包分析 请求头参数加密 二、逆向分析 下xhr断点 参数生成位置 发现是AES加密&#xff0c;不过是混淆的&#xff0c;但并不影响咱们扣代码 文章仅提供技术交流学习&#xff0c;不可对目标服…

Vue3+Element Plus:使用el-dialog,对话框可拖动,且对话框弹出时仍然能够在背景页(对话框外部的页面部分)上进行滚动以及输入框输入信息

【需求】 使用Element Plus中的el-dialog默认是模态的&#xff08;即它会阻止用户与对话框外部的元素进行交互&#xff09;&#xff0c;对话框弹出时仍然能够在背景页&#xff08;对话框外部的页面部分&#xff09;上进行滚动以及输入框输入信息&#xff0c;且对话框可拖动 【…

react hooks--React.memo

基本语法 React.memo 高阶组件的使用场景说明&#xff1a; React 组件更新机制&#xff1a;只要父组件状态更新&#xff0c;子组件就会无条件的一起更新。 子组件 props 变化时更新过程&#xff1a;组件代码执行 -> JSX Diff&#xff08;配合虚拟 DOM&#xff09;-> 渲…

STM32精确控制步进电机

目的&#xff1a;学习使用STM32电机驱动器步进电机&#xff0c;进行电机运动精确控制。 测试环境&#xff1a; MCU主控芯片STM32F103RCT6 &#xff1b;A4988步进电机驱动器模块&#xff1b;微型2相4线步进电机10mm丝杆滑台&#xff0c;金属丝杆安装有滑块。 10mm二相四线微型…

NtripShare测量机器人自动化监测系统测站更换仪器后重新设站

NtripShare测量机器人自动化监测系统投入商业运营已经很久了&#xff0c;在MosBox与自动优化网平差技术的加持下&#xff0c;精度并不让人担心&#xff0c;最近基于客户需求处理了两个比较大的问题。 1、增加对反射片和免棱镜的支持。 2、进一步优化测站更换仪器或重新整平后重…

Qemu开发ARM篇-5、buildroot制作根文件系统并在qemu中进行挂载启动

文章目录 1、 buildroot源码获取2、buildroot配置3、buildroot编译4、挂载根文件系统 在上一篇 Qemu开发ARM篇-4、kernel交叉编译运行演示中&#xff0c;我们编译了kernel&#xff0c;并在qemu上进行了运行&#xff0c;但到最后&#xff0c;在挂载根文件系统时候&#xff0c;挂…

[数据集][目标检测]文本表格检测数据集VOC+YOLO格式6688张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6688 标注数量(xml文件个数)&#xff1a;6688 标注数量(txt文件个数)&#xff1a;6688 标注…

Fyne ( go跨平台GUI )中文文档-绘图和动画(三)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…

Easy Excel从入门到精通!!!

目录 1.文件导入 1.1基本方式读取excel文件内容 1.2注解模型映射器读取excel 1.3多行表头读取 1.4文件上传读取 2.文件导出 2.1基本方式导出 2.2模型映射导出 2.3设置行高、列宽等内容 2.4合并单元格 2.5导出设置超链接、批注、公式 2.6模板填充对象导出 2.7模板填…

Mybatis 返回 Map 对象

一、场景介绍 假设有如下一张学生表&#xff1a; CREATE TABLE student (id int NOT NULL AUTO_INCREMENT COMMENT 主键,name varchar(100) NOT NULL COMMENT 姓名,gender varchar(10) NOT NULL COMMENT 性别,grade int NOT NULL COMMENT 年级,PRIMARY KEY (id) ) ENGINEInnoD…

【C++篇】引领C++模板初体验:泛型编程的力量与妙用

文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程&#xff1f;1.1.1 为什么要有泛型编程&#xff1f;1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板&#xff1f;1.2.2 函数模板的定义格式1.2.3 示例&#xff1a;通用的交换函数输出示例&am…

华为HarmonyOS地图服务 11 - 如何在地图上增加点注释?

场景介绍 本章节将向您介绍如何在地图的指定位置添加点注释以标识位置、商家、建筑等&#xff0c;并可以通过信息窗口展示详细信息。 点注释支持功能&#xff1a; 支持设置图标、文字、碰撞规则等。支持添加点击事件。 PointAnnotation有默认风格&#xff0c;同时也支持自定…

文献阅读(220)MRCN

题目&#xff1a;MRCN: Throughput-Oriented Multicast Routing for Customized Network-on-Chips时间&#xff1a;2023期刊&#xff1a;TPDS研究机构&#xff1a;韩国成均馆大学 这篇论文探讨的问题是多播死锁问题&#xff0c;下图中Packet A分成两条路径&#xff0c;但在rou…

Leetcode—1014. 最佳观光组合【中等】

2024每日刷题&#xff08;164&#xff09; Leetcode—1014. 最佳观光组合 实现代码 class Solution { public:int maxScoreSightseeingPair(vector<int>& values) {int mxPre values[0] 0;int ans 0;for(int i 1; i < values.size(); i) {ans max(ans, mxP…

python绘制弦图-科研作图

一、背景 弦图以其直观、精美的展示方式受到越来越多人的关注&#xff0c;它不仅能够有效展示两个变量之间的联系&#xff0c;还能同时展现多个变量间的复杂互动&#xff0c;本文将通过Python语言中的pycirclize库&#xff0c;带你深入了解如何绘制弦图。 弦图是一种圆…

51单片机——矩阵键盘

一、矩阵键盘原理图 我们发现: P17,P16,P15,P14控制行&#xff0c; P13,P12,P11,P10控制列。 所以我们如果要选择第四列&#xff0c;只需要把整个P1先给高电位1&#xff0c;再把P10给低电位0。 二、代码 P10xFF; P100; if(P170){Delay(20);while(P170);Delay(20);KeyNum…

计算机毕业设计python+spark知识图谱房价预测系统 房源推荐系统 房源数据分析 房源可视化 房源大数据大屏 大数据毕业设计 机器学习

《PythonSpark知识图谱房价预测系统》开题报告 一、研究背景与意义 随着城市化进程的加速和房地产市场的不断发展&#xff0c;房价成为影响人们生活质量的重要因素之一。准确预测房价不仅有助于政府制定科学的房地产政策&#xff0c;还能为开发商提供市场参考&#xff0c;同时…

DriveMatriX Highway Dataset :高速公路驾驶数据集(猫脸码客 第196期)

DriveMatriX Highway Dataset 1.0&#xff1a;自动驾驶与ADAS感知验证的里程碑 在当今快速发展的自动驾驶&#xff08;AV&#xff09;和高级驾驶辅助系统&#xff08;ADAS&#xff09;领域&#xff0c;数据的获取与处理成为了推动技术进步的关键因素。为了在这些复杂且多变的交…

【软件测试】Bug 篇

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 今天给大家带来的是 【软件测试】Bug 篇&#xff0c;首先了解, 什么是Bug, 如何定义一个Bug, 如何描述一个 Bug, Bug的级别, 和 Bug 的生命周期, 以及测试人员跟开发人员产生争执如何处理,…

【Linux】常用指令【更详细,带实操】

Linux全套讲解系列&#xff0c;参考视频-B站韩顺平&#xff0c;本文的讲解更为详细 目录 一、文件目录指令 1、cd【change directory】指令 ​ 2、mkdir【make dir..】指令​ 3、cp【copy】指令 ​ 4、rm【remove】指令 5、mv【move】指令 6、cat指令和more指令 7、less和…