CPU密集型任务开发指导

2024-02-16 13:45 更新

CPU密集型任务是指需要占用系统资源处理大量计算能力的任务,需要长时间运行,这段时间会阻塞线程其它事件的处理,不适宜放在主线程进行。例如图像处理、视频编码、数据分析等。

基于多线程并发机制处理CPU密集型任务可以提高CPU利用率,提升应用程序响应速度。

当进行一系列同步任务时,推荐使用Worker;而进行大量或调度点较为分散的独立任务时,不方便使用8个Worker去做负载管理,推荐采用TaskPool。接下来将以图像直方图处理以及后台长时间的模型预测任务分别进行举例。

使用TaskPool进行图像直方图处理

  1. 实现图像处理的业务逻辑。

  2. 数据分段,将各段数据通过不同任务的执行完成图像处理。

    创建Task,通过execute()执行任务,在当前任务结束后,会将直方图处理结果同时返回。

  3. 结果数组汇总处理。

  1. import taskpool from '@ohos.taskpool';
  2. @Concurrent
  3. function imageProcessing(dataSlice: ArrayBuffer) {
  4. // 步骤1: 具体的图像处理操作及其他耗时操作
  5. return dataSlice;
  6. }
  7. function histogramStatistic(pixelBuffer: ArrayBuffer) {
  8. // 步骤2: 分成三段并发调度
  9. let number = pixelBuffer.byteLength / 3;
  10. let buffer1 = pixelBuffer.slice(0, number);
  11. let buffer2 = pixelBuffer.slice(number, number * 2);
  12. let buffer3 = pixelBuffer.slice(number * 2);
  13. let task1 = new taskpool.Task(imageProcessing, buffer1);
  14. let task2 = new taskpool.Task(imageProcessing, buffer2);
  15. let task3 = new taskpool.Task(imageProcessing, buffer3);
  16. taskpool.execute(task1).then((ret: ArrayBuffer[]) => {
  17. // 步骤3: 结果处理
  18. });
  19. taskpool.execute(task2).then((ret: ArrayBuffer[]) => {
  20. // 步骤3: 结果处理
  21. });
  22. taskpool.execute(task3).then((ret: ArrayBuffer[]) => {
  23. // 步骤3: 结果处理
  24. });
  25. }
  26. @Entry
  27. @Component
  28. struct Index {
  29. @State message: string = 'Hello World'
  30. build() {
  31. Row() {
  32. Column() {
  33. Text(this.message)
  34. .fontSize(50)
  35. .fontWeight(FontWeight.Bold)
  36. .onClick(() => {
  37. let data: ArrayBuffer;
  38. histogramStatistic(data);
  39. })
  40. }
  41. .width('100%')
  42. }
  43. .height('100%')
  44. }
  45. }

使用Worker进行长时间数据分析

本文通过某地区提供的房价数据训练一个简易的房价预测模型,该模型支持通过输入房屋面积和房间数量去预测该区域的房价,模型需要长时间运行,房价预测需要使用前面的模型运行结果,因此需要使用Worker。

  1. DevEco Studio提供了Worker创建的模板,新建一个Worker线程,例如命名为“MyWorker”。

  2. 在主线程中通过调用ThreadWorker的constructor()方法创建Worker对象,当前线程为宿主线程。

    1. import worker from '@ohos.worker';
    2. const workerInstance = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts');
  3. 在宿主线程中通过调用onmessage()方法接收Worker线程发送过来的消息,并通过调用postMessage()方法向Worker线程发送消息。

    例如向Worker线程发送训练和预测的消息,同时接收Worker线程发送回来的消息。

    1. // 接收Worker子线程的结果
    2. workerInstance.onmessage = function(e) {
    3. // data:Worker线程发送的信息
    4. let data = e.data;
    5. console.info('MyWorker.ts onmessage');
    6. }
    7. workerInstance.onerror = function (d) {
    8. // 接收Worker子线程的错误信息
    9. }
    10. // 向Worker子线程发送训练消息
    11. workerInstance.postMessage({ 'type': 0 });
    12. // 向Worker子线程发送预测消息
    13. workerInstance.postMessage({ 'type': 1, 'value': [90, 5] });
  4. 在MyWorker.ts文件中绑定Worker对象,当前线程为Worker线程。

    1. import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
    2. let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
  5. 在Worker线程中通过调用onmessage()方法接收宿主线程发送的消息内容,并通过调用postMessage()方法向宿主线程发送消息。

    例如在Worker线程中定义预测模型及其训练过程,同时与主线程进行信息交互。

    1. import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
    2. let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
    3. // 定义训练模型及结果
    4. let result;
    5. // 定义预测函数
    6. function predict(x) {
    7. return result[x];
    8. }
    9. // 定义优化器训练过程
    10. function optimize() {
    11. result = {};
    12. }
    13. // Worker线程的onmessage逻辑
    14. workerPort.onmessage = function (e: MessageEvents) {
    15. let data = e.data
    16. // 根据传输的数据的type选择进行操作
    17. switch (data.type) {
    18. case 0:
    19. // 进行训练
    20. optimize();
    21. // 训练之后发送主线程训练成功的消息
    22. workerPort.postMessage({ type: 'message', value: 'train success.' });
    23. break;
    24. case 1:
    25. // 执行预测
    26. const output = predict(data.value);
    27. // 发送主线程预测的结果
    28. workerPort.postMessage({ type: 'predict', value: output });
    29. break;
    30. default:
    31. workerPort.postMessage({ type: 'message', value: 'send message is invalid' });
    32. break;
    33. }
    34. }
  6. 在Worker线程中完成任务之后,执行Worker线程销毁操作。销毁线程的方式主要有两种:根据需要可以在宿主线程中对Worker线程进行销毁;也可以在Worker线程中主动销毁Worker线程。

    在宿主线程中通过调用onexit()方法定义Worker线程销毁后的处理逻辑。

    1. // Worker线程销毁后,执行onexit回调方法
    2. workerInstance.onexit = function() {
    3. console.info("main thread terminate");
    4. }

    方式一:在宿主线程中通过调用terminate()方法销毁Worker线程,并终止Worker接收消息。

    1. // 销毁Worker线程
    2. workerInstance.terminate();

    方式二:在Worker线程中通过调用close()方法主动销毁Worker线程,并终止Worker接收消息。

    1. // 销毁线程
    2. workerPort.close();
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号