助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!
多線程併發
對於剛接觸多線程編程的新手來説,理解進程、線程、併發和並行等基礎概念是至關重要的。在深入探討多線程併發之前,讓我們先統一這些基礎概念的認識,為後續學習打下堅實的基礎。
基本概念
進程
- 進程是操作系統分配資源(CPU、內存、文件等)和獨立運行程序的基本單位,每個進程擁有隔離的內存空間和系統資源,是正在執行的程序的實例。
線程
- 線程是操作系統能夠調度的最小執行單元,是進程(Process)中的一個獨立控制流。
- 多線程 是指一個進程中運行多個線程,共享進程資源(如文件句柄、內存),但各自獨立執行。
併發和並行
- 併發:多個任務交替執行(單核 CPU 分時調度),看似“同時”運行,實則
- 並行:多個任務真正同時執行(需多核 CPU 或多台機器)
[!TIP]
學習多線程的核心目的是 通過併發執行任務來提升程序性能與響應能力,充分利用CPU的計算能力。
線程併發模型
線程併發模型是協調多線程執行的編程範式,旨在提升性能但需解決競態與死鎖問題。常見的併發模型有基於內存共享的模型和基於消息通信的模型。
內存共享模型
內存共享併發模型指多線程同時執行任務,這些線程依賴同一內存資源並且都有權限訪問,線程訪問內存前需要搶佔並鎖定內存的使用權,沒有搶佔到內存的線程需要等待其他線程釋放使用權再執行。
助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!
消息通信模型
Actor併發模型是基於消息通信的典型併發模型。它使開發者無需處理鎖帶來的複雜問題,且具備高併發度,因此應用廣泛。
Actor併發模型與內存共享併發模型相比,不同線程間的內存是隔離的,因此不會發生線程競爭同一內存資源的情況。無需處理內存上鎖問題,從而提高開發效率。Actor併發模型中,不同Actor之間不共享內存,需通過消息傳遞機制傳遞任務和結果。
以下示例簡單展示瞭如何使用基於Actor模型的併發能力來解決生產者消費者問題
Actor模型中,不同角色之間並不共享內存,生產者線程和UI線程都有自己的虛擬機實例,兩個虛擬機實例之間擁有獨佔的內存,相互隔離。生產者生產出結果後,通過序列化通信將結果發送給UI線程。UI線程消費結果後,再發送新的生產任務給生產者線程。
助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!
[!Tip]
當前ArkTS提供了
TaskPool和Worker兩種併發能力,兩者均基於Actor併發模型實現。
助理鴻蒙生態建設,歡迎成為鴻蒙開發者,免費獲取鴻蒙開發者認證,歡迎留言領考試學習資料,免費!免費!
TaskPool
任務池(taskpool)的作用是為應用程序提供多線程運行環境,降低資源消耗並提升系統性能,且您無需關心線程實例的生命週期。您可以使用任務池API創建後台任務(Task),並進行如執行任務或取消任務等操作。理論上,任務池API允許創建的任務數量不受限制,但由於內存限制,不建議這樣做。此外,不建議在任務中執行阻塞操作,尤其是無限期阻塞操作,因為長時間的阻塞操作會佔用工作線程,可能阻塞其他任務的調度,影響應用性能。
您所創建的同一優先級任務的執行順序可以由您決定,任務真實執行的順序與您調用任務池API提供的任務執行接口順序一致。任務默認優先級是MEDIUM。
當同一時間待執行的任務數量大於任務池工作線程數量,任務池會根據負載均衡機制進行擴容,增加工作線程數量,減少整體等待時長。同樣,當執行的任務數量減少,工作線程數量大於執行任務數量,部分工作線程處於空閒狀態,任務池會根據負載均衡機制進行縮容,減少工作線程數量。
TaskPool運作機制
TaskPool支持在宿主線程提交任務到任務隊列,系統選擇合適的工作線程執行任務,並將結果返回給宿主線程。接口易用,支持任務執行、取消和指定優先級。通過系統統一線程管理,結合動態調度和負載均衡算法,可以節約系統資源。系統默認啓動一個任務工作線程,任務多時會自動擴容。工作線程數量上限由設備的物理核數決定,內部管理具體數量,確保調度和執行效率最優。長時間無任務分發時會縮容,減少工作線程數量。
TaskPool運作機制示意圖
@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
示例2:async併發函數
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
對鴻蒙感興趣的同學,免費考取鴻蒙開發者認證