助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!

多線程併發

對於剛接觸多線程編程的新手來説,理解進程、線程、併發和並行等基礎概念是至關重要的。在深入探討多線程併發之前,讓我們先統一這些基礎概念的認識,為後續學習打下堅實的基礎。

基本概念

進程

  • 進程是操作系統分配資源(CPU、內存、文件等)和獨立運行程序的基本單位,每個進程擁有隔離的內存空間和系統資源,是正在執行的程序的實例

線程

  • 線程是操作系統能夠調度的最小執行單元,是進程(Process)中的一個獨立控制流。
  • 多線程 是指一個進程中運行多個線程,共享進程資源(如文件句柄、內存),但各自獨立執行。

併發和並行

  • 併發:多個任務交替執行(單核 CPU 分時調度),看似“同時”運行,實則
  • 並行:多個任務真正同時執行(需多核 CPU 或多台機器)

[!TIP]

學習多線程的核心目的是 通過併發執行任務來提升程序性能與響應能力,充分利用CPU的計算能力。

線程併發模型

線程併發模型是協調多線程執行的編程範式,旨在提升性能但需解決競態與死鎖問題。常見的併發模型有基於內存共享的模型和基於消息通信的模型

內存共享模型

內存共享併發模型指多線程同時執行任務,這些線程依賴同一內存資源並且都有權限訪問,線程訪問內存前需要搶佔並鎖定內存的使用權,沒有搶佔到內存的線程需要等待其他線程釋放使用權再執行。

HarmonyOS鴻蒙多線程併發之TaskPool快速上手_開發者

助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!

消息通信模型

Actor併發模型是基於消息通信的典型併發模型。它使開發者無需處理鎖帶來的複雜問題,且具備高併發度,因此應用廣泛。

Actor併發模型與內存共享併發模型相比,不同線程間的內存是隔離的,因此不會發生線程競爭同一內存資源的情況。無需處理內存上鎖問題,從而提高開發效率。Actor併發模型中,不同Actor之間不共享內存,需通過消息傳遞機制傳遞任務和結果。

以下示例簡單展示瞭如何使用基於Actor模型的併發能力來解決生產者消費者問題

Actor模型中,不同角色之間並不共享內存,生產者線程和UI線程都有自己的虛擬機實例,兩個虛擬機實例之間擁有獨佔的內存,相互隔離。生產者生產出結果後,通過序列化通信將結果發送給UI線程。UI線程消費結果後,再發送新的生產任務給生產者線程。

HarmonyOS鴻蒙多線程併發之TaskPool快速上手_工作線程_02

助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!


[!Tip]

當前ArkTS提供了TaskPoolWorker兩種併發能力,兩者均基於Actor併發模型實現。

助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!

TaskPool

任務池(taskpool)的作用是為應用程序提供多線程運行環境,降低資源消耗並提升系統性能,且您無需關心線程實例的生命週期。您可以使用任務池API創建後台任務(Task),並進行如執行任務或取消任務等操作。理論上,任務池API允許創建的任務數量不受限制,但由於內存限制,不建議這樣做。此外,不建議在任務中執行阻塞操作,尤其是無限期阻塞操作,因為長時間的阻塞操作會佔用工作線程,可能阻塞其他任務的調度,影響應用性能。

您所創建的同一優先級任務的執行順序可以由您決定,任務真實執行的順序與您調用任務池API提供的任務執行接口順序一致。任務默認優先級是MEDIUM。

當同一時間待執行的任務數量大於任務池工作線程數量,任務池會根據負載均衡機制進行擴容,增加工作線程數量,減少整體等待時長。同樣,當執行的任務數量減少,工作線程數量大於執行任務數量,部分工作線程處於空閒狀態,任務池會根據負載均衡機制進行縮容,減少工作線程數量。

TaskPool運作機制

TaskPool支持在宿主線程提交任務到任務隊列,系統選擇合適的工作線程執行任務,並將結果返回給宿主線程。接口易用,支持任務執行、取消和指定優先級。通過系統統一線程管理,結合動態調度和負載均衡算法,可以節約系統資源。系統默認啓動一個任務工作線程,任務多時會自動擴容。工作線程數量上限由設備的物理核數決定,內部管理具體數量,確保調度和執行效率最優。長時間無任務分發時會縮容,減少工作線程數量。

TaskPool運作機制示意圖

HarmonyOS鴻蒙多線程併發之TaskPool快速上手_併發模型_03

@Concurrent裝飾器

@Concurrent用於裝飾一個函數為併發函數,使用Taskpool時,執行的併發函數必須用該裝飾器修飾,否則無法通過校驗。

@Concurrent允許裝飾普通函數和async函數,禁止標註箭頭函數、類方法。不支持類成員函數或者匿名函數。

示例1:普通併發函數

import { taskpool } from '@kit.ArkTS'

//示例:併發函數為一個計算兩數之和的普通函數,`taskpool`執行該函數並返回結果。
@Concurrent
function concurrentFunc(num1: number, num2: number): number {
  return num1 + num2;
}

//執行併發函數,方式一
taskpool.execute<[number, number], number>(concurrentFunc, 1, 1).then((result)=>{
  console.log(`task1 resut is ${result}`)  //輸出:taskpool resut is 2
})

//執行併發函數,方式二
let task = new taskpool.Task(concurrentFunc, 2, 2)
taskpool.execute<[number, number], number>(task).then((result)=>{
  console.log(`task1 resut is ${result}`)  //輸出:taskpool resut is 4
})

//值併發函數,方式3
async function executeConcurrentFunc(){
  try {
    let result = await taskpool.execute(concurrentFunc, 3, 3)
    console.log(`task1 resut is ${result}`) //輸出:taskpool resut is 6
  } catch (e) {
    console.log(`task1 error is ${e}`)  //如果併發函數執行錯誤,會拋出異常,被catch捕獲
  }
}

executeConcurrentFunc()

輸出結果如下

task1 resut is 2
task1 resut is 4
task1 resut is 6

示例2async併發函數

import { taskpool } from '@kit.ArkTS'

//示例:async併發函數
@Concurrent
async function asyncConcurrentFunc(array: number[]): Promise<number> {
  console.log(`task2...start`)
  let sum = 0
  for (let i = 1; i <= array.length; i++) {
    console.log(`task2...${i}`)
    sum += i
  }
  console.log(`task2...end`)
  return await Promise.resolve(sum) //await作用:等待執行完後再返回結果. 此處不能直接返回 Promise,否則會出異常
}

//執行async併發函數
taskpool.execute<[number[]], number>(asyncConcurrentFunc,[1,2,3,4,5,6]).then((result) => {
  console.log(`task2 resut is ${result}`) //輸出:task2 resut is 7
})
.catch((error: Error) => {
  console.log(`task2 error is ${error}`) //
})

輸出結果如下

task2...start
task2...1
task2...2
...
task2...100
task2...end
task2 resut is 21

對鴻蒙感興趣的同學,免費考取鴻蒙開發者認證