【Day 8】鸿蒙中多线程的开发

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、鸿蒙多线程基础概念

  1. 线程与进程的区别

    • 进程:资源分配的最小单位(如一个鸿蒙应用就是一个进程)
    • 线程:CPU调度的最小单位(如UI渲染、网络请求等任务线程)

    A[应用进程] --> B[主线程-UI渲染]
    A --> C[Worker线程-计算任务]
    A --> D[TaskPool线程-轻量任务]
  2. 为什么需要多线程?

    • 主线程限制:UI操作必须在主线程执行,耗时任务会引发卡顿 
    • 性能优化:多核CPU并行处理(如同时下载多个文件) 
       

二、TaskPool基础详解

1. 核心特性
特性 说明 对比Worker
自动生命周期管理 无需手动创建/销毁线程,由系统调度 Worker需手动terminate
优先级控制 支持HIGH/MEDIUM/LOW三级优先级 Worker无优先级概念 
轻量级任务 适合3分钟内的短任务(如网络请求、简单计算) 适合长耗时任务 

2. 基础使用四步法

// 1. 定义被@Concurrent修饰的全局函数
@Concurrent
function add(a: number, b: number): number {
    return a + b  // 此代码在子线程执行
}

// 2. 创建任务
const task = new taskpool.Task(add, 1, 2)

// 3. 执行任务(可设置优先级)
taskpool.execute(task, taskpool.Priority.HIGH).then((result) => {
    console.log(`结果: ${result}`)  // 主线程回调
})

// 4. 取消任务(可选)
taskpool.cancel(task)  // 终止任务

3.完整使用示例 (并发计数器(1-100与101-200求和)

import taskpool from '@ohos.taskpool'

// 定义并发计算函数(需@Concurrent修饰)
@Concurrent
function calculateSum(start: number, end: number): number {
  let sum = 0
  for (let i = start; i <= end; i++) {
    sum += i
  }
  return sum
}

@Entry
@Component
struct ConcurrentCounter {
  @State totalSum: number = 0
  @State isCalculating: boolean = false

  build() {
    Column() {
      Button('开始计算')
        .onClick(() => this.startCalculation())
        .margin(10)
        .width('80%')

      Text(`总和结果: ${this.totalSum}`)
        .fontSize(20)
        .margin(10)

      if (this.isCalculating) {
        Progress()
          .width('80%')
      }
    }
    .width('100%')
    .height('100%')
  }

  // 启动并发计算
  async startCalculation() {
    this.isCalculating = true
    this.totalSum = 0

    try {
      // 创建两个Task任务
      const task1 = new taskpool.Task(calculateSum, 1, 100)
      const task2 = new taskpool.Task(calculateSum, 101, 200)

      // 并行执行任务
      const [sum1, sum2] = await Promise.all([
        taskpool.execute(task1),
        taskpool.execute(task2)
      ])

      // 主线程更新结果
      this.totalSum = sum1 + sum2
    } catch (e) {
      console.error(`计算失败: ${e}`)
    } finally {
      this.isCalculating = false
    }
  }
}
关键点解析
  1. @Concurrent函数

    • 必须为全局函数,且仅支持基础数据类型参数(如number/string) 
    • 函数内禁止使用闭包或外部变量,所有数据通过参数传递 
  2. TaskPool任务管理

    • 通过taskpool.Task创建任务实例,传入函数及参数 
    • taskpool.execute()返回Promise,可用await获取结果 
  3. 主线程更新UI

    • 计算结果通过@State变量驱动UI刷新
    • 使用Promise.all确保所有任务完成后汇总结果 
  4. 错误处理

    • 捕获可能的序列化或线程异常(如参数类型错误) 

三、Worker的使用

1. 文件准备
  • 路径规则:Worker文件必须放在{moduleName}/src/main/ets/workers/目录下,例如: entry/src/main/ets/workers/FileDownloadWorker.ets 注:路径错误会导致打包失败 
2. 自动生成模板

在DevEco Studio中:

  1. 右键点击entry/src/main/ets → New → Worker
  2. 输入文件名(如FileDownloadWorker),IDE会自动生成以下内容:
    • Worker文件模板
    • build-profile.json5中的配置项
{
  "buildOption": {
    "sourceOption": {
      "workers": ["./src/main/ets/workers/FileDownloadWorker.ets"]
    }
  }
}

3.主线程与Worker通信示例

import { worker } from '@kit.ArkTS'

@Entry
@Component
struct DownloadPage {
  private downloadWorker: worker.ThreadWorker

  aboutToAppear() {
    // 初始化Worker
    this.downloadWorker = new worker.ThreadWorker(
      'entry/ets/workers/FileDownloadWorker.ets',
      { name: 'DownloadWorker' } // 可选命名
    )

    // 监听Worker消息
    this.downloadWorker.onmessage = (e: worker.MessageEvents) => {
      console.log('下载进度:', e.data.progress)
    }

    // 监听错误
    this.downloadWorker.onerror = (e: worker.ErrorEvent) => {
      console.error('Worker异常:', e.message)
    }
  }

  // 启动下载任务
  startDownload() {
    this.downloadWorker.postMessage({ 
      url: 'https://example.com/file.zip',
      chunkSize: 1024 
    })
  }

  // 销毁Worker(必须!)
  aboutToDisappear() {
    this.downloadWorker.terminate()
  }
}

 4. Worker线程代码示例

import { worker, http } from '@kit.ArkTS'

const workerPort = worker.workerPort

// 处理主线程消息
workerPort.onmessage = async (e: worker.MessageEvents) => {
  const { url, chunkSize } = e.data
  const req = http.createHttp()
  
  // 分块下载示例
  const response = await req.request(url, { range: `bytes=0-${chunkSize}` })
  
  // 回传进度
  workerPort.postMessage({ 
    progress: 50,
    data: response.result 
  })
}

// 错误处理(可选)
workerPort.onerror = (e: Error) => {
  console.error('子线程错误:', e)
}

关键注意事项

  1. 生命周期管理

    • 必须调用terminate()主动销毁Worker,否则持续占用内存 
    • 建议在页面aboutToDisappear或组件销毁时清理Worker
  2. 通信限制

    • 传输数据需可序列化,大小≤16MB 
    • 禁止传递函数或DOM对象(与Web Worker一致) 
  3. 性能优化

    • 复用Worker实例(避免频繁创建/销毁)
    • 大数据分片处理(如文件下载分块) 

你可能感兴趣的:(harmonyos,华为,鸿蒙)