摘要:本文从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的事件队列投递任务。

典型流程如下:

  1. 创建EventRunner:通常在新创建的子线程中启动一个EventRunner(EventRunner.create() + run())。
  2. 创建EventHandler:在需要投递任务的线程(如主线程)中,创建绑定到目标EventRunner的EventHandler实例。
  3. 投递任务:使用EventHandler向目标EventRunner的事件队列投递任务。

五、总结

理解线程、进程与CPU调度机制是掌握鸿蒙系统开发的基础。CPU通过多核与超线程技术实现并行处理,进程是资源隔离的基本单位,线程是实际执行的基本单位。鸿蒙系统通过严格的进程隔离保障安全与稳定,同时提供公共事件、Emitter、TaskPool Worker、Worker、EventHandler等多种机制实现跨进程与线程间的数据交互。开发者在实际开发中,应根据业务场景合理选择进程模型和通信方式,以构建高效、稳定的鸿蒙应用。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐