关于harmonyos:HarmonyOS使用多线程并发能力开发

38次阅读

共计 4773 个字符,预计需要花费 12 分钟才能阅读完成。

一、多线程并发概述

1、简介
并发模型是用来实现不同利用场景中并发工作的编程模型,常见的并发模型分为基于内存共享的并发模型和基于音讯通信的并发模型。

Actor 并发模型作为基于音讯通信并发模型的典型代表,不须要开发者去面对锁带来的一系列简单偶发的问题,同时并发度也绝对较高,因而失去了宽泛的反对和应用,也是以后 ArkTS 语言选择的并发模型。

因为 Actor 模型的内存隔离个性,所以须要进行跨线程的数据序列化传输。

2、数据传输对象
目前反对传输的数据对象能够分为一般对象、可转移对象、可共享对象、Native 绑定对象四种。

● 一般对象

一般对象传输采纳规范的结构化克隆算法(Structured Clone)进行序列化,此算法能够通过递归的形式拷贝传输对象,相较于其余序列化的算法,反对的对象类型更加丰盛。

序列化反对的类型包含:除 Symbol 之外的根底类型、Date、String、RegExp、Array、Map、Set、Object(仅限简略对象,比方通过“{}”或者“new Object”创立,一般对象仅反对传递属性,不反对传递其原型及办法)、ArrayBuffer、TypedArray。

● 可转移对象
可转移对象(Transferable object)传输采纳地址转移进行序列化,不须要内容拷贝,会将 ArrayBuffer 的所有权转移给接管该 ArrayBuffer 的线程,转移后该 ArrayBuffer 在发送它的线程中变为不可用,不容许再拜访。

// 定义可转移对象
let buffer = new ArrayBuffer(100);

● 可共享对象
共享对象 SharedArrayBuffer,领有固定长度,能够存储任何类型的数据,包含数字、字符串等。

共享对象传输指 SharedArrayBuffer 反对在多线程之间传递,传递之后的 SharedArrayBuffer 对象和原始的 SharedArrayBuffer 对象能够指向同一块内存,进而达到内存共享的目标。

SharedArrayBuffer 对象存储的数据在同时被批改时,须要通过原子操作保障其同步性,即下个操作开始之前务必须要等到上个操作曾经完结。

// 定义可共享对象,能够应用 Atomics 进行操作
let sharedBuffer = new SharedArrayBuffer(1024);

● Native 绑定对象
Native 绑定对象(Native Binding Object)是零碎所提供的对象,该对象与底层零碎性能进行绑定,提供间接拜访底层零碎性能的能力。

以后反对序列化传输的 Native 绑定对象次要蕴含:Context 和 RemoteObject。

Context 对象蕴含应用程序组件的上下文信息,它提供了一种拜访零碎服务和资源的形式,使得应用程序组件能够与零碎进行交互。获取 Context 信息的办法能够参考获取上下文信息。

RemoteObject 对象的次要作用是实现近程通信的性能,它容许在不同的过程间传递对象的援用,使得不同过程之间能够共享对象的状态和办法,服务提供者必须继承此类,RemoteObject 对象的创立能够参考 RemoteObject 的实现。

3、TaskPool 和 WorkerArkTS
提供了 TaskPool 和 Worker 两种并发能力供开发者抉择,其具体的实现特点和各自的实用场景存在差别,具体请参见 TaskPool 和 Worker 的比照。

二、TaskPool 和 Worker 的比照

TaskPool(工作池)和 Worker 的作用是为应用程序提供一个多线程的运行环境,用于解决耗时的计算工作或其余密集型工作。能够无效地防止这些工作阻塞主线程,从而最大化零碎的利用率,升高整体资源耗费,并进步零碎的整体性能。

本文将从实现特点和实用场景两个方面来进行 TaskPool 与 Worker 的比拟,同时提供了各自运作机制和注意事项的相干阐明。

1.  实现特点比照表
表 1 TaskPool 和 Worker 的实现特点比照

2.  实用场景比照
TaskPool 和 Worker 均反对多线程并发能力。TaskPool 偏差独立工作(线程级)维度;而 Worker 偏差线程的维度,反对长时间占据线程执行。

常见的一些开发场景及实用具体阐明如下:
● 有关联的一系列同步工作。例如某数据库操作时,要用创立的句柄操作,蕴含增、删、改、查多个工作,要保障同一个句柄,须要应用 Worker。
● 须要频繁勾销的工作。例如图库大图浏览场景,为晋升体验,会同时缓存以后图片左右侧各 2 张图片,往一侧滑动跳到下一张图片时,要勾销另一侧的一个缓存工作,须要应用 TaskPool。
● 大量或者调度点较扩散的工作。例如大型利用的多个模块蕴含多个耗时工作,不方便使用 8 个 Worker 去做负载治理,举荐采纳 TaskPool。

3.  TaskPool 运作机制
图 1 TaskPool 运作机制示意图

TaskPool 反对开发者在主线程封装工作抛给工作队列,零碎抉择适合的工作线程,进行工作的散发及执行,再将后果返回给主线程。接口直观易用,反对工作的执行、勾销。工作线程数量下限为 4。

4.  Worker 运作机制
图 2 Worker 运作机制示意图

创立 Worker 的线程称为宿主线程(不肯定是主线程,工作线程也反对创立 Worker 子线程),Worker 本身的线程称为 Worker 子线程(或 Actor 线程、工作线程)。每个 Worker 子线程与宿主线程领有独立的实例,蕴含基础设施、对象、代码段等。Worker 子线程和宿主线程之间的通信是基于消息传递的,Worker 通过序列化机制与宿主线程之间互相通信,实现命令及数据交互。

5.  TaskPool 注意事项
● 实现工作的函数须要应用装璜器 @Concurrent 标注,且仅反对在.ets 文件中应用。
● 实现工作的函数只反对一般函数或者 async 函数,不反对类成员函数或者匿名函数。
● 实现工作的函数仅反对在 Stage 模型的工程中应用 import 的变量和入参变量,否则只能应用入参变量。
● 实现工作的函数入参需满足序列化反对的类型,详情请参见一般对象传输。
● 因为不同线程中上下文对象是不同的,因而 TaskPool 工作线程只能应用线程平安的库,例如 UI 相干的非线程平安库不能应用。
● 序列化传输的数据量大小限度为 16MB。

6.  Worker 注意事项
● 创立 Worker 时,传入的 Worker.ts 门路在不同版本有不同的规定,详情请参见文件门路注意事项。
● Worker 创立后须要手动治理生命周期,且最多同时运行的 Worker 子线程数量为 8 个,详情请参见生命周期注意事项。
● Ability 类型的 Module 反对应用 Worker,Library 类型的 Module 不反对应用 Worker。
● 创立 Worker 不反对应用其余 Module 的 Worker.ts 文件,即不反对跨模块调用 Worker。
● 因为不同线程中上下文对象是不同的,因而 Worker 线程只能应用线程平安的库,例如 UI 相干的非线程平安库不能应用。
● 序列化传输的数据量大小限度为 16MB。

文件门路注意事项
当应用 Worker 模块具体性能时,均需先结构 Worker 实例对象,其构造函数与 API 版本相干。

// API 9 及之后版本应用:const worker1 = new worker.ThreadWorker(scriptURL);
// API 8 及之前版本应用:const worker1 = new worker.Worker(scriptURL);

构造函数须要传入 Worker 的门路(scriptURL),Worker 文件寄存地位默认门路为 Worker 文件所在目录与 pages 目录属于同级。

Stage 模型
构造函数中的 scriptURL 示例如下:/

// 写法一
// Stage 模型 - 目录同级(entry 模块下,workers 目录与 pages 目录同级)const worker1 = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts', {name:"first worker in Stage model"});
// Stage 模型 - 目录不同级(entry 模块下,workers 目录是 pages 目录的子目录)const worker2 = new worker.ThreadWorker('entry/ets/pages/workers/MyWorker.ts');

// 写法二
// Stage 模型 - 目录同级(entry 模块下,workers 目录与 pages 目录同级),假如 bundlename 是 com.example.workerdemo
const worker3 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/workers/worker');
// Stage 模型 - 目录不同级(entry 模块下,workers 目录是 pages 目录的子目录),假如 bundlename 是 com.example.workerdemo
const worker4 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/pages/workers/worker');

● 基于 Stage 模型工程目录构造,写法一的门路含意:
○ entry:module.json5 文件中 module 的 name 属性对应值。
○ ets:用于寄存 ets 源码,固定目录。
○ workers/MyWorker.ts:worker 源文件在 ets 目录下的门路。

● 基于 Stage 模型工程目录构造,写法二的门路含意:
○ @bundle:固定标签。
○ bundlename:以后利用包名。
○ entryname:module.json5 文件中 module 的 name 属性对应值。
○ ets:用于寄存 ets 源码,固定目录。
○ workerdir/workerfile:worker 源文件在 ets 目录下的门路,可不带文件后缀名。

FA 模型
构造函数中的 scriptURL 示例如下:

// FA 模型 - 目录同级(entry 模块下,workers 目录与 pages 目录同级)const worker1 = new worker.ThreadWorker('workers/worker.js', {name:'first worker in FA model'});
// FA 模型 - 目录不同级(entry 模块下,workers 目录与 pages 目录的父目录同级)const worker2 = new worker.ThreadWorker('../workers/worker.js');

生命周期注意事项

● Worker 的创立和销毁消耗性能,倡议开发者正当治理已创立的 Worker 并重复使用。Worker 闲暇时也会始终运行,因而当不须要 Worker 时,能够调用 terminate()接口或 parentPort.close()办法被动销毁 Worker。若 Worker 处于已销毁或正在销毁等非运行状态时,调用其性能接口,会抛出相应的谬误。

● Worker 存在数量限度,反对最多同时存在 8 个 Worker。
○ 在 API version 8 及之前的版本,当 Worker 数量超出限度时,会抛出“Too many workers, the number of workers exceeds the maximum.”谬误。
○ 从 API version 9 开始,当 Worker 数量超出限度时,会抛出“Worker initialization failure, the number of workers exceeds the maximum.”谬误。

正文完
 0