鸿蒙基础:进程、线程与通讯机制详解
摘要:本文从CPU调度原理出发,系统讲解线程与进程的核心概念,深入剖析鸿蒙系统中的进程分类与隔离机制,并重点介绍鸿蒙线程与进程间的数据交互方式,帮助开发者理解鸿蒙系统的底层运行机制。
一、线程、进程与CPU
在计算机中,CPU是资源调度的核心,目前我们使用的CPU通常会这样描述:8核32位处理器。其中的8核指的就是8个物理CPU逻辑单元,32位指的是CPU处理集的位数,我们程序要抢占的就是CPU的逻辑执行单元,随着当前科技的发展,一个CPU的物理逻辑单元通常允许同时加载两个线程,这就是并行处理。
回到我们的程序中,我们程序在系统上构建好后就是完整的一个进程,在系统中我们可以看见应用被分配了一个独立的进程号,这个进程号证明我们应用在系统中成功运行并存活,是应用的存活线,而线程是实际逻辑处理单位,我们实际让CPU处理的就是线程。
1.1 进程与线程的区别
进程是操作系统进行资源分配和调度的基本单位,拥有独立的地址空间、文件描述符和系统资源;而线程是进程内部的实际执行单元,是CPU调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的地址空间和系统资源,但各自拥有独立的栈和寄存器上下文。
简单来说,进程是"资源容器",线程是"执行流"。进程之间相互隔离,一个进程崩溃不会影响其他进程;而同一进程内的多个线程则共享资源,一个线程的异常可能导致整个进程崩溃。
1.2 CPU多核与并行处理
现代CPU普遍采用多核架构,每个物理核心可以独立执行指令。以8核处理器为例,它拥有8个物理计算单元,可以同时处理8条指令流。而超线程技术(Hyper-Threading)进一步让每个物理核心模拟出两个逻辑核心,使得支持超线程技术的8核处理器可以同时处理16个线程,这就是我们常说的"8核16线程"。
并行处理的核心价值在于:当程序需要执行大量独立计算任务时,可以将任务拆分为多个线程,分配到不同的CPU核心上同时执行,从而大幅提升整体计算效率。但需要注意的是,并行处理并非万能,当任务之间存在依赖关系或需要频繁同步时,多线程反而可能带来额外的调度开销。
二、鸿蒙系统中的进程
鸿蒙(HarmonyOS)作为面向全场景的分布式操作系统,其进程与线程模型设计兼顾了性能优化与用户体验。在鸿蒙系统中,进程是系统进行资源分配的基本单位,是操作系统结构的基础。一个应用下通常包含三类进程:主进程、ExtensionAbility进程和WebView渲染进程。
2.1 鸿蒙进程分类详解
鸿蒙系统为应用提供了多种进程类型,每种进程承担不同的职责:
| 进程类型 | 说明 |
|---|---|
| 主进程 | 应用的核心进程,承载主要业务逻辑,负责应用界面的渲染和用户交互。 |
| ExtensionAbility进程 | 同一种类型的ExtensionAbility会放在同一个进程里。例如创建了两个FormExtensionAbility,也会在同一个进程中运行。 |
| WebView渲染进程 | 用于网页容器渲染的进程,这种设计提升了鸿蒙系统中ArkWeb的网页容器渲染能力。 |
在进程的创建与限制方面,三方应用可以通过创建XXExtensionAbility的形式增加应用内的进程。但需要注意的是,WebView渲染进程和主进程不能创建增加。
2.2 进程隔离机制
鸿蒙系统采用严格的进程隔离策略,每个进程拥有独立的虚拟地址空间,进程间不能直接访问对方的内存数据。这种隔离机制带来两大好处:
- 安全性:即使某个进程被恶意攻击或发生崩溃,也无法直接读取或破坏其他进程的数据,有效防止数据泄露和系统崩溃。
- 稳定性:单个进程的异常不会影响其他进程的正常运行,系统整体稳定性得到保障。
需要注意的是,虽然同一应用的不同进程在逻辑上属于同一个应用,但它们在系统层面依然是相互隔离的独立进程,数据不能直接共享。
三、鸿蒙线程模型
线程是操作系统进行运算调度的基本单位,是进程中的执行流,共享进程的资源。在鸿蒙系统中,以主进程为例,包含三种类型的线程:
| 线程类型 | 用途 |
|---|---|
| 主线程 | 处理UI绘制、事件分发等操作,负责与用户界面相关的所有操作,包括布局计算、渲染以及屏幕刷新等。 |
| TaskPool Worker线程 | 主要用于处理轻量级的耗时任务。 |
| Worker线程 | 用于偏重的耗时任务。 |
主线程(UI线程)负责处理与用户界面相关的所有操作,包括布局计算、渲染以及屏幕刷新等。在鸿蒙系统中,ArkTS引擎用于管理主线程上的UI渲染。
四、鸿蒙通信机制
由于进程间相互隔离,鸿蒙系统提供了多种跨进程数据交互机制,以满足应用不同场景下的通信需求。
4.1 跨进程通信
鸿蒙系统提供两种主要方式实现跨进程通信:
| 通信方式 | 说明 |
|---|---|
| 公共事件(CommonEvent) | 主要监听系统的事件,分为系统公共事件和自定义事件。三方应用主要通过自定义事件来通信。 |
| Emitter | 类似于EventHub,但在此基础上多了传递包裹的封装,添加了优先级和序号,也新增了once(监听一次)的接口。 |
此外,startAbility也可传递一些信息。
公共事件示例代码:
import { commonEventManager } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 定义订阅者,用于保存创建成功的订阅者对象
let subscriber: commonEventManager.CommonEventSubscriber;
// 订阅者信息
let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
events: ["event"]
};
// 创建订阅者回调
function createCB(err: BusinessError, commonEventSubscriber: commonEventManager.CommonEventSubscriber) {
if (err != null) {
console.error(`Failed to create subscriber. Code is ${err.code}, message is ${err.message}`);
} else {
console.info(`Succeeded in creating subscriber`);
subscriber = commonEventSubscriber;
}
}
// 创建订阅者
commonEventManager.createSubscriber(subscribeInfo, createCB);
Emitter示例代码:
@Sendable
class Sample {
constructor() {
this.count = 100;
}
printCount() {
console.info('Print count : ' + this.count);
}
count: number;
}
let callback = (eventData: emitter.GenericEventData<Sample>): void => {
let storage: Sample = eventData.data!;
storage.printCount();
}
// 收到eventId为"eventId"的事件后执行回调函数
emitter.on("eventId", callback);
4.2 线程间通信
线程间通信主要使用 TaskPool Worker 和 Worker 创建线程,并配套双向通信的接口。
TaskPool Worker:主要用于处理轻量级耗时任务。需要注意的是,过于耗时的操作(如超过三分钟)会无条件失败。
@Concurrent
function printArgs(args: number): number {
console.info("printArgs: " + args);
return args;
}
taskpool.execute(printArgs, 100).then((value: Object) => {
// 100: test number
console.info("taskpool result: " + value);
});
Worker:用于偏重的耗时任务。由于消耗系统资源,一个应用只能创建八个。
// Worker.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
// 创建worker线程中与宿主线程通信的对象
const workerPort = worker.workerPort
// worker线程接收宿主线程信息
workerPort.onmessage = (e: MessageEvents): void => {
// data:宿主线程发送的信息
let data: number = e.data;
// 往收到的buffer里写入数据
const view = new Int8Array(data).fill(3);
// worker线程向宿主线程发送信息
workerPort.postMessage(view);
};
// worker线程发生error的回调
workerPort.onerror = (err: ErrorEvent) => {
console.log("worker.ets onerror" + err.message);
};
// Index.ets
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
// 宿主线程中创建Worker对象
const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");
// 宿主线程向worker线程传递信息
const buffer = new ArrayBuffer(8);
workerInstance.postMessage(buffer, [buffer]);
// 宿主线程接收worker线程信息
workerInstance.onmessage = (e: MessageEvents): void => {
// data:worker线程发送的信息
let data: number = e.data;
console.info("main thread data is " + 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);
}
})
}
.width('100%')
.height('100%')
}
}
}
4.3 线程内通信
线程内通信可通过以下方式实现:
- EventHub:用于UI界面、UIAbility和业务类之间的通信。
- GlobalThis:全局对象共享数据。
- 单例:通过单例模式共享数据。
- AppStorage:应用级数据存储。
4.4 EventHandler与EventRunner
鸿蒙还提供了更底层、灵活的事件循环和任务投递机制:
| 组件 | 说明 |
|---|---|
| EventRunner | 一个事件循环器(Event Loop),运行在独立线程上,持续从其关联的EventQueue中取出事件(InnerEvent)或任务(Runnable)并执行。 |
| EventHandler | 绑定到特定EventRunner(即绑定到特定线程)的事件处理器,通过它向该EventRunner的事件队列投递任务。 |
典型流程如下:
- 创建EventRunner:通常在新创建的子线程中启动一个EventRunner(EventRunner.create() + run())。
- 创建EventHandler:在需要投递任务的线程(如主线程)中,创建绑定到目标EventRunner的EventHandler实例。
- 投递任务:使用EventHandler向目标EventRunner的事件队列投递任务。
五、总结
理解线程、进程与CPU调度机制是掌握鸿蒙系统开发的基础。CPU通过多核与超线程技术实现并行处理,进程是资源隔离的基本单位,线程是实际执行的基本单位。鸿蒙系统通过严格的进程隔离保障安全与稳定,同时提供公共事件、Emitter、TaskPool Worker、Worker、EventHandler等多种机制实现跨进程与线程间的数据交互。开发者在实际开发中,应根据业务场景合理选择进程模型和通信方式,以构建高效、稳定的鸿蒙应用。
更多推荐


所有评论(0)