鸿蒙ArkTS是如何实现并发的?

1.什么是并发?

并发指在同一时间内,存在多个任务同时执行的情况。

  • 多核设备:这些任务可能同时在不同CPU上并行执行
  • 单核设备:多个并发任务不会在同一时刻并行执行,但是CPU会在某个任务休眠或进行I/O操作等状态下切换任务调度执行其他任务,提升CPU的资源利用率。

并发核心概念

  • 并发与并行
    • 并发:逻辑上的"同时执行"(单核通过任务切换实现,多核可真正并行)。
    • 并行:物理上的同时执行(依赖多核设备)。
  • 目标:提升响应速度与帧率,避免主线程阻塞。

2.为了提升应用的响应速度与帧率,避免耗时任务对UI主线程的影响,ArkTS提供了集中策略?

异步并发

异步代码在执行到一定程度后会被暂停,以便在未来某个时间点继续执行,这种情况下,同一时间只有一段代码在执行。ArkTS通过Promise和async/await提供异步并发能力,适用于单次I/O任务的开发场景。

多线程并发

允许在同一时间段内同时执行多段代码。在UI主线程继续响应用户操作和更新UI的同时,后台线程也能执行耗时操作,从而避免应用出现卡顿。ArkTS通过TaskPool和Worker提供多线程并发能力,适用于耗时任务等并发场景。

3.ArkTS并发策略对比

策略 异步并发 多线程并发
实现方式 Promise + async/await TaskPool(线程池) + Worker(独立线程)
执行特性 单线程,代码分段暂停/恢复 多线程并行执行
适用场景 单次I/O任务(如网络请求、文件读写) CPU密集型任务、耗时操作(如图像处理)
线程阻塞 不阻塞主线程,但代码串行执行 后台线程执行,完全避免主线程卡顿
资源开销 低(无线程创建开销) 较高(需管理线程生命周期)

关键要点归总

  1. 异步并发

    • 本质:单线程任务调度,通过事件循环实现非阻塞。
    • 优势:轻量级,适合简单I/O任务。
    • 限制 :无法利用多核性能,复杂任务可能导致回调嵌套(需async/await优化)。
  2. 多线程并发

    • 核心对象
      • TaskPool:线程池复用,避免频繁创建/销毁线程。
      • Worker:独立线程,适合长时任务。
    • 数据传输
      • 基本类型:直接拷贝传输。
      • 复杂对象 :需序列化(如JSON)或共享内存(谨慎使用)。
    • 注意:线程间通信需通过消息传递,避免共享资源竞争。

4.应用场景选择

任务类型 推荐策略 示例场景
单次I/O操作 异步并发 请求API、读取本地文件
CPU密集型 多线程(TaskPool) 图像处理、数据加密解密
长时任务 多线程(Worker) 后台下载、持续日志写入
UI交互响应 主线程 + 异步 按钮点击后非阻塞更新UI

5.异步并发概述 (Promise和async/await)

异步并发核心概念

  1. 本质

    • 单线程非阻塞任务调度,同一时间仅执行一段代码
    • 通过事件循环(Event Loop)实现任务挂起与恢复,避免主线程阻塞。
  2. 适用场景

    • 单次I/O操作(如网络请求、文件读写)
    • 不适用于CPU密集型任务(会阻塞主线程)

Promise与async/await对比

特性 Promise async/await
本质 异步状态管理对象 Promise的语法糖,简化异步代码编写
代码风格 链式调用(.then().catch() 同步式写法,逻辑更直观
错误处理 通过.catch()捕获异常 通过try/catch捕获异常
返回值 返回Promise对象 返回Promise对象
可读性 回调嵌套复杂时较差 线性执行,可读性高

Promise核心要点

  1. 三种状态

    • pending(进行中) → fulfilled(成功) 或 rejected(失败)
    • 状态不可逆 :一旦变为fulfilledrejected,不可再改变。
  2. 基本用法

    typescript 复制代码
    const promise = new Promise((resolve, reject) => { 
      // 异步操作(如setTimeout、文件读写)
      if (成功) resolve(result); 
      else reject(error); 
    });
    • 链式调用

      typescript 复制代码
      promise.then(result => { ... })
        .catch(error => { ... });
  3. 关键注意

    • 未处理的reject 会触发unhandledrejection事件 → 需全局监听:

      typescript 复制代码
      errorManager.on('error', (err) => { ... });
完整例子如下,以下代码创建了一个Promise对象并模拟了一个异步操作:
typescript 复制代码
const promise: Promise<number> = new Promise((resolve: Function, reject: Function) => {
setTimeout(() => { 
const randomNumber: number = Math.random(); 
if (randomNumber > 0.5) { 
resolve(randomNumber); 
} else {    
reject(new Error('Random number is too small'));  }}, 1000);})

上述代码中,setTimeout函数模拟了一个异步操作,并在1秒钟后随机生成一个数字。如果随机数大于0.5,则执行resolve回调函数并将随机数作为参数传递;否则执行reject回调函数并传递一个错误对象作为参数。


async/await核心要点

  1. 语法规则

    • 函数声明 :用async标记异步函数(如async function fetchData()
    • 等待Promise :用await暂停执行,直到Promise完成(await promise
  2. 代码示例

    typescript 复制代码
    async function myAsyncFunc(): Promise<string> {
      try {
        const result = await new Promise(resolve => {
          setTimeout(() => resolve('Hello'), 3000);
        });
        return result;
      } catch (error) {
        console.error(error);
        throw error; // 抛出异常会被外层catch捕获
      }
    }
  3. 优势与限制

    • 代码扁平化:避免回调地狱(Callback Hell)
    • 滥用风险 :若在循环中误用await,可能导致性能下降
完整例子如下,下面是一个使用async/await的例子,其中模拟了一个以同步方式执行异步操作的场景,该操作会在3秒钟后返回一个字符串。
typescript 复制代码
async function myAsyncFunction(): Promise<string> { 
const result: string = await new Promise((resolve: Function) => {   
setTimeout(() => {     
resolve('Hello, world!');    }, 3000);  });
console.info(result); // 输出: Hello, world!  return result;}
@Entry@Componentstruct Index { 
@State message: string = 'Hello World'; 
build() {   
Row() {    
Column() {      
Text(this.message)        
.fontSize(50)         
.fontWeight(FontWeight.Bold)          
.onClick(async () => {            
let res = await myAsyncFunction();            
console.info("res is: " + res);         
})}      
.width('100%')
}    
.height('100%') 
}}

错误处理对比

方式 Promise async/await
成功处理 .then(result => { ... }) const result = await promise
失败处理 .catch(error => { ... }) try { ... } catch (error) { ... }
全局异常捕获 监听unhandledrejection事件 结合try/catch与全局监听

6.多线程并发(TaskPool和Worker)

任务池(TaskPool)

作用是为应用程序提供一个多线程的运行环境,降低整体资源的消耗、提高系统的整体性能,且您无需关心线程实例的生命周期。具体接口信息及使用方法详情请见TaskPool

TaskPool运作机制

Worker

主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与宿主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞宿主线程的运行。具体接口信息及使用方法详情请见Worker

Worker运作机制

TaskPool vs Worker 核心对比

特性 TaskPool Worker
本质 线程池动态调度(任务队列 + 线程复用) 独立线程(需手动管理生命周期)
适用场景 短时、高频的CPU密集型任务(如计算) 长时、独立的后台任务(如下载、日志)
线程数量 动态扩容(上限=设备物理核数) 最多64个,需手动销毁
开发复杂度 低(系统自动管理线程) 较高(需处理线程创建、销毁、通信)
数据传输限制 16MB(支持序列化对象) 16MB(支持序列化对象)
生命周期 任务结束自动释放线程资源 需手动调用close()terminate()销毁
错误处理 通过Promise的.catch()捕获 通过onerror回调捕获

TaskPool核心要点

1. 使用场景

  • CPU密集型短任务:如图像处理、加密解密。
  • 高频小任务:避免频繁创建线程的开销。

2. 关键规则

  • 装饰器 :任务函数必须用@Concurrent修饰。
  • 执行时间:单任务函数耗时≤3分钟(不含异步I/O)。
  • 数据传输:仅支持可序列化类型(如基本类型、ArrayBuffer)。

3. 示例代码

typescript 复制代码
// 定义任务函数(必须@Concurrent)
@Concurrent
function add(a: number, b: number): number {
  return a + b;
}

// 提交任务到线程池
async function runTask() {
  const task = new taskpool.Task(add, 1, 2);
  const result = await taskpool.execute(task);
  console.log(`Task结果: ${result}`); // 输出3
}

4. 注意事项

  • 禁止闭包:任务函数内不能访问外部变量。
  • 线程安全库:只能使用线程安全的API(如不可操作UI)。

Worker核心要点

1. 使用场景

  • 长时任务:如后台下载、持续数据同步。
  • 独立线程需求:需明确控制线程生命周期。

2. 关键规则

  • 线程数量:最多同时运行64个Worker。
  • 生命周期 :必须手动调用close()销毁。
  • 文件路径 :Worker文件需放在指定目录(如entry/ets/workers/)。

3. 示例代码

typescript 复制代码
// 主线程:创建Worker并通信
const worker = new worker.ThreadWorker('entry/ets/workers/myWorker.ets');
worker.postMessage('开始任务'); // 发送消息
worker.onmessage = (e) => { 
  console.log(`收到Worker回复: ${e.data}`); 
};

// Worker线程(myWorker.ets)
workerPort.onmessage = (e) => {
  workerPort.postMessage('任务完成'); // 回复消息
};

4. 注意事项

  • 多级Worker:父Worker销毁前需先销毁子Worker。
  • 内存限制:所有Worker内存总和≤1.5GB或物理内存60%。

场景选择口诀

  • TaskPool扛短快,Worker长时独立在;

  • 高频计算用池化,下载日志Worker带。


错误处理对比

策略 错误捕获方式 示例
TaskPool 通过Promise的.catch() taskpool.execute(task).catch(e => {})
Worker 通过onerror回调 worker.onerror = (err) => { ... }

记忆要点

  1. TaskPool

    • 线程池复用,适合短任务。
    • @Concurrent装饰器 + 可序列化参数。
  2. Worker

    • 独立线程,手动管理生命周期。
    • 主从线程通过postMessage通信。

TaskPool和Worker的具体实现特点对比可以参考官方文档

相关推荐
鸿蒙布道师1 小时前
鸿蒙NEXT开发数值工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
塞尔维亚大汉6 小时前
鸿蒙南向开发 ——轻量系统芯片移植指南(二)
物联网·嵌入式·harmonyos
别说我什么都不会7 小时前
OpenHarmony内核系统调用hats测试用例编写指南
物联网·嵌入式·harmonyos
鸿蒙布道师10 小时前
鸿蒙NEXT开发日期工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
HMSCore12 小时前
在应用内购票、寄件时,如何一键填充所需信息?
harmonyos
HarmonyOS_SDK12 小时前
在应用内购票、寄件时,如何一键填充所需信息?
harmonyos
枫叶丹414 小时前
【HarmonyOS Next之旅】DevEco Studio使用指南(十一)
华为·harmonyos·deveco studio·harmonyos next
H_MY14 小时前
鸿蒙 —— 系统图标大全
harmonyos
"孙小浩15 小时前
HarmonyOS应用开发者高级-编程题-001
华为·wpf·harmonyos