TaskPool和Worker的對比

2024-02-16 13:44 更新

TaskPool(任務(wù)池)和Worker的作用是為應(yīng)用程序提供一個多線程的運(yùn)行環(huán)境,用于處理耗時(shí)的計(jì)算任務(wù)或其他密集型任務(wù)。可以有效地避免這些任務(wù)阻塞主線程,從而最大化系統(tǒng)的利用率,降低整體資源消耗,并提高系統(tǒng)的整體性能。

本文將從實(shí)現(xiàn)特點(diǎn)適用場景兩個方面來進(jìn)行TaskPool與Worker的比較,同時(shí)提供了各自運(yùn)作機(jī)制和注意事項(xiàng)的相關(guān)說明。

實(shí)現(xiàn)特點(diǎn)對比

表1 TaskPool和Worker的實(shí)現(xiàn)特點(diǎn)對比

實(shí)現(xiàn)

TaskPool

Worker

內(nèi)存模型

線程間隔離,內(nèi)存不共享。

線程間隔離,內(nèi)存不共享。

參數(shù)傳遞機(jī)制

采用標(biāo)準(zhǔn)的結(jié)構(gòu)化克隆算法(Structured Clone)進(jìn)行序列化、反序列化,完成參數(shù)傳遞。

支持ArrayBuffer轉(zhuǎn)移和SharedArrayBuffer共享。

采用標(biāo)準(zhǔn)的結(jié)構(gòu)化克隆算法(Structured Clone)進(jìn)行序列化、反序列化,完成參數(shù)傳遞。

支持ArrayBuffer轉(zhuǎn)移和SharedArrayBuffer共享。

參數(shù)傳遞

直接傳遞,無需封裝,默認(rèn)進(jìn)行transfer。

消息對象唯一參數(shù),需要自己封裝。

方法調(diào)用

直接將方法傳入調(diào)用。

在Worker線程中進(jìn)行消息解析并調(diào)用對應(yīng)方法。

返回值

異步調(diào)用后默認(rèn)返回。

主動發(fā)送消息,需在onmessage解析賦值。

生命周期

TaskPool自行管理生命周期,無需關(guān)心任務(wù)負(fù)載高低。

開發(fā)者自行管理Worker的數(shù)量及生命周期。

任務(wù)池個數(shù)上限

自動管理,無需配置。

同個進(jìn)程下,最多支持同時(shí)開啟8個Worker線程。

任務(wù)執(zhí)行時(shí)長上限

無限制。

無限制。

設(shè)置任務(wù)的優(yōu)先級

不支持。

不支持。

執(zhí)行任務(wù)的取消

支持取消任務(wù)隊(duì)列中等待的任務(wù)。

不支持。

適用場景對比

TaskPool偏向獨(dú)立任務(wù)維度,該任務(wù)在線程中執(zhí)行,無需關(guān)注線程的生命周期,超長任務(wù)(大于3分鐘)會被系統(tǒng)自動回收;而Worker偏向線程的維度,支持長時(shí)間占據(jù)線程執(zhí)行,需要主動管理線程生命周期。

常見的一些開發(fā)場景及適用具體說明如下:

  • 有關(guān)聯(lián)的一系列同步任務(wù)。例如在一些需要創(chuàng)建、使用句柄的場景中,句柄創(chuàng)建每次都是不同的,該句柄需永久保存,保證使用該句柄進(jìn)行操作,需要使用Worker。
  • 需要頻繁取消的任務(wù)。例如圖庫大圖瀏覽場景,為提升體驗(yàn),會同時(shí)緩存當(dāng)前圖片左右側(cè)各2張圖片,往一側(cè)滑動跳到下一張圖片時(shí),要取消另一側(cè)的一個緩存任務(wù),需要使用TaskPool。
  • 大量或者調(diào)度點(diǎn)較分散的任務(wù)。例如大型應(yīng)用的多個模塊包含多個耗時(shí)任務(wù),不方便使用8個Worker去做負(fù)載管理,推薦采用TaskPool。

TaskPool運(yùn)作機(jī)制

圖1 TaskPool運(yùn)作機(jī)制示意圖

TaskPool支持開發(fā)者在主線程封裝任務(wù)拋給任務(wù)隊(duì)列,系統(tǒng)選擇合適的工作線程,進(jìn)行任務(wù)的分發(fā)及執(zhí)行,再將結(jié)果返回給主線程。接口直觀易用,支持任務(wù)的執(zhí)行、取消。工作線程數(shù)量上限為4。

Worker運(yùn)作機(jī)制

圖2 Worker運(yùn)作機(jī)制示意圖

創(chuàng)建Worker的線程稱為宿主線程(不一定是主線程,工作線程也支持創(chuàng)建Worker子線程),Worker自身的線程稱為Worker子線程(或Actor線程、工作線程)。每個Worker子線程與宿主線程擁有獨(dú)立的實(shí)例,包含基礎(chǔ)設(shè)施、對象、代碼段等。Worker子線程和宿主線程之間的通信是基于消息傳遞的,Worker通過序列化機(jī)制與宿主線程之間相互通信,完成命令及數(shù)據(jù)交互。

TaskPool注意事項(xiàng)

  • 實(shí)現(xiàn)任務(wù)的函數(shù)需要使用裝飾器@Concurrent標(biāo)注,且僅支持在.ets文件中使用。

  • 實(shí)現(xiàn)任務(wù)的函數(shù)入?yún)⑿铦M足序列化支持的類型,詳情請參見數(shù)據(jù)傳輸對象。

  • 由于不同線程中上下文對象是不同的,因此TaskPool工作線程只能使用線程安全的庫,例如UI相關(guān)的非線程安全庫不能使用。

  • 序列化傳輸?shù)臄?shù)據(jù)量大小限制為16MB。

Worker注意事項(xiàng)

  • 創(chuàng)建Worker時(shí),傳入的Worker.ts路徑在不同版本有不同的規(guī)則,詳情請參見文件路徑注意事項(xiàng)。

  • Worker創(chuàng)建后需要手動管理生命周期,且最多同時(shí)運(yùn)行的Worker子線程數(shù)量為8個,詳情請參見生命周期注意事項(xiàng)。

  • Ability類型的Module支持使用Worker,Library類型的Module不支持使用Worker。

  • 創(chuàng)建Worker不支持使用其他Module的Worker.ts文件,即不支持跨模塊調(diào)用Worker。

  • 由于不同線程中上下文對象是不同的,因此Worker線程只能使用線程安全的庫,例如UI相關(guān)的非線程安全庫不能使用。

  • 序列化傳輸?shù)臄?shù)據(jù)量大小限制為16MB。

文件路徑注意事項(xiàng)

當(dāng)使用Worker模塊具體功能時(shí),均需先構(gòu)造Worker實(shí)例對象,其構(gòu)造函數(shù)與API版本相關(guān)。

  1. // 導(dǎo)入模塊
  2. import worker from '@ohos.worker';
  3. // API 9及之后版本使用:
  4. const worker1 = new worker.ThreadWorker(scriptURL);
  5. // API 8及之前版本使用:
  6. const worker1 = new worker.Worker(scriptURL);

構(gòu)造函數(shù)需要傳入Worker的路徑(scriptURL),Worker文件存放位置默認(rèn)路徑為Worker文件所在目錄與pages目錄屬于同級。

Stage模型

構(gòu)造函數(shù)中的scriptURL示例如下:

  1. // 導(dǎo)入模塊
  2. import worker from '@ohos.worker';
  3. // 寫法一
  4. // Stage模型-目錄同級(entry模塊下,workers目錄與pages目錄同級)
  5. const worker1 = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts', {name:"first worker in Stage model"});
  6. // Stage模型-目錄不同級(entry模塊下,workers目錄是pages目錄的子目錄)
  7. const worker2 = new worker.ThreadWorker('entry/ets/pages/workers/MyWorker.ts');
  8. // 寫法二
  9. // Stage模型-目錄同級(entry模塊下,workers目錄與pages目錄同級),假設(shè)bundlename是com.example.workerdemo
  10. const worker3 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/workers/worker');
  11. // Stage模型-目錄不同級(entry模塊下,workers目錄是pages目錄的子目錄),假設(shè)bundlename是com.example.workerdemo
  12. const worker4 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/pages/workers/worker');
  • 基于Stage模型工程目錄結(jié)構(gòu),寫法一的路徑含義:

    • entry:module.json5文件中module的name屬性對應(yīng)值。
    • ets:用于存放ets源碼,固定目錄。
    • workers/MyWorker.ts:worker源文件在ets目錄下的路徑。
  • 基于Stage模型工程目錄結(jié)構(gòu),寫法二的路徑含義:

    • @bundle:固定標(biāo)簽。
    • bundlename:當(dāng)前應(yīng)用包名。
    • entryname:module.json5文件中module的name屬性對應(yīng)值。
    • ets:用于存放ets源碼,固定目錄。
    • workerdir/workerfile:worker源文件在ets目錄下的路徑,可不帶文件后綴名。

FA模型

構(gòu)造函數(shù)中的scriptURL示例如下:

  1. // 導(dǎo)入模塊
  2. import worker from '@ohos.worker';
  3. // FA模型-目錄同級(entry模塊下,workers目錄與pages目錄同級)
  4. const worker1 = new worker.ThreadWorker('workers/worker.js', {name:'first worker in FA model'});
  5. // FA模型-目錄不同級(entry模塊下,workers目錄與pages目錄的父目錄同級)
  6. const worker2 = new worker.ThreadWorker('../workers/worker.js');

生命周期注意事項(xiàng)

  • Worker的創(chuàng)建和銷毀耗費(fèi)性能,建議開發(fā)者合理管理已創(chuàng)建的Worker并重復(fù)使用。Worker空閑時(shí)也會一直運(yùn)行,因此當(dāng)不需要Worker時(shí),可以調(diào)用terminate()接口或parentPort.close()方法主動銷毀Worker。若Worker處于已銷毀或正在銷毀等非運(yùn)行狀態(tài)時(shí),調(diào)用其功能接口,會拋出相應(yīng)的錯誤。

  • Worker存在數(shù)量限制,支持最多同時(shí)存在8個Worker。

    • 在API version 8及之前的版本,當(dāng)Worker數(shù)量超出限制時(shí),會拋出“Too many workers, the number of workers exceeds the maximum.”錯誤。
    • 從API version 9開始,當(dāng)Worker數(shù)量超出限制時(shí),會拋出“Worker initialization failure, the number of workers exceeds the maximum.”錯誤。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號