1. CPU を集中的に使用するタスクの開発ガイダンス
CPU 負荷の高いタスクとは、大量のコンピューティング能力を処理するためにシステム リソースを必要とし、長時間実行する必要があるタスクを指します。この間、スレッド内の他のイベントの処理がブロックされるため、スレッドでの実行には適していません。メインスレッド。例えば、画像処理、ビデオコーディング、データ分析などです。
マルチスレッド同時実行メカニズムに基づいて CPU 負荷の高いタスクを処理すると、CPU 使用率とアプリケーションの応答速度が向上します。
一連の同期タスクを実行する場合は Worker を使用することをお勧めしますが、多数の独立したタスクを実行する場合やスケジューリング ポイントが分散している場合は、負荷管理のために 8 個の Worker を使用するのは不便であるため、TaskPool を使用することを推奨します。次に、バックグラウンドでの画像ヒストグラム処理と長期モデル予測タスクの例を示します。
TaskPoolを使用した画像ヒストグラム処理
- 画像処理のためのビジネスロジックを実装します。
- データのセグメント化。データの各セグメントはさまざまなタスクを通じて実行され、画像処理を完了します。
Taskを作成し、 execute()でタスクを実行すると、現在のタスクが終了すると、同時にヒストグラムの処理結果が返されます。
3. 結果配列の要約処理。
import taskpool from '@ohos.taskpool';
@Concurrent
function imageProcessing(dataSlice: ArrayBuffer) {
// 步骤1: 具体的图像处理操作及其他耗时操作
return dataSlice;
}
function histogramStatistic(pixelBuffer: ArrayBuffer) {
// 步骤2: 分成三段并发调度
let number = pixelBuffer.byteLength / 3;
let buffer1 = pixelBuffer.slice(0, number);
let buffer2 = pixelBuffer.slice(number, number * 2);
let buffer3 = pixelBuffer.slice(number * 2);
let task1 = new taskpool.Task(imageProcessing, buffer1);
let task2 = new taskpool.Task(imageProcessing, buffer2);
let task3 = new taskpool.Task(imageProcessing, buffer3);
taskpool.execute(task1).then((ret: ArrayBuffer[]) => {
// 步骤3: 结果处理
});
taskpool.execute(task2).then((ret: ArrayBuffer[]) => {
// 步骤3: 结果处理
});
taskpool.execute(task3).then((ret: ArrayBuffer[]) => {
// 步骤3: 结果处理
});
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let data: ArrayBuffer;
histogramStatistic(data);
})
}
.width('100%')
}
.height('100%')
}
}
Workers を使用した長期データ分析
この記事では、特定の地域から提供される住宅価格データを使用して、住宅面積と部屋数を入力することで、その地域の住宅価格の予測をサポートする簡単な住宅価格予測モデルを学習します。モデルは長時間実行する必要があり、住宅価格の予測には以前のモデルの実行結果を使用する必要があるため、Worker を使用する必要があります。
- DevEco Studio は、たとえば「MyWorker」という名前の新しい Worker スレッドを作成するために Worker によって作成されたテンプレートを提供します。
2. ThreadWorker のconstructor()メソッド を呼び出して、メイン スレッドに Worker オブジェクトを作成します(現在のスレッドがホスト スレッドになります)。
import worker from '@ohos.worker';
const workerInstance = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts');
3. ホスト スレッドで、 onmessage()メソッドを呼び出してワーカー スレッドによって送信されたメッセージを受信し、 postMessage()メソッドを呼び出してメッセージをワーカー スレッドに送信します。
たとえば、トレーニング メッセージと予測メッセージをワーカー スレッドに送信し、同時にワーカー スレッドから返信されたメッセージを受信します。
// 接收Worker子线程的结果
workerInstance.onmessage = function(e) {
// data:主线程发送的信息
let data = e.data;
console.info('MyWorker.ts onmessage');
// 在Worker线程中进行耗时操作
}
workerInstance.onerror = function (d) {
// 接收Worker子线程的错误信息
}
// 向Worker子线程发送训练消息
workerInstance.postMessage({
'type': 0 });
// 向Worker子线程发送预测消息
workerInstance.postMessage({
'type': 1, 'value': [90, 5] });
4. MyWorker.ts ファイル内の Worker オブジェクトをバインドすると、現在のスレッドが Worker スレッドになります。
import worker, {
ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
5. ワーカー スレッドで、 onmessage()メソッドを呼び出してホスト スレッドによって送信されたメッセージ コンテンツを受信し、 postMessage()メソッドを呼び出してメッセージをホスト スレッドに送信します。
たとえば、予測モデルとそのトレーニング プロセスはワーカー スレッドで定義され、情報は同時にメイン スレッドとやり取りされます。
import worker, {
ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
// 定义训练模型及结果
let result;
// 定义预测函数
function predict(x) {
return result[x];
}
// 定义优化器训练过程
function optimize() {
result = {};
}
// Worker线程的onmessage逻辑
workerPort.onmessage = function (e: MessageEvents) {
let data = e.data
// 根据传输的数据的type选择进行操作
switch (data.type) {
case 0:
// 进行训练
optimize();
// 训练之后发送主线程训练成功的消息
workerPort.postMessage({
type: 'message', value: 'train success.' });
break;
case 1:
// 执行预测
const output = predict(data.value);
// 发送主线程预测的结果
workerPort.postMessage({
type: 'predict', value: output });
break;
default:
workerPort.postMessage({
type: 'message', value: 'send message is invalid' });
break;
}
}
6. ワーカー スレッドのタスクが完了したら、ワーカー スレッドの破棄操作を実行します。スレッドを破棄するには主に 2 つの方法があります: 必要に応じてホスト スレッドでワーカー スレッドを破棄することも、ワーカー スレッドでアクティブにワーカー スレッドを破棄することもできます。
ホスト スレッドでonexit()メソッドを呼び出して、ワーカー スレッドが破棄された後の処理ロジックを定義します。
// Worker线程销毁后,执行onexit回调方法
workerInstance.onexit = function() {
console.info("main thread terminate");
}
方法 1: ホスト スレッドでterminate()メソッドを呼び出してワーカースレッドを破棄し、ワーカーのメッセージ受信を終了します。
// 销毁Worker线程
workerInstance.terminate();
方法 2: ワーカー スレッドでclose()メソッドを呼び出してワーカー スレッドをアクティブに破棄し、ワーカーのメッセージ受信を終了します。
// 销毁线程
workerPort.close();
2. I/O 集約型タスク開発ガイダンス
非同期同時実行を使用すると、単一の I/O タスクをブロックする問題を解決できます。ただし、I/O 集中型のタスクが発生すると、スレッド内の他のタスクの実行もブロックされます。この場合、マルチスレッド同時実行問題を解決するには、さまざまな機能を使用する必要があります。
I/O 集中型タスクのパフォーマンスの焦点は、通常、CPU の処理能力ではなく、I/O 操作の速度と効率にあります。この種のタスクでは通常、ディスクの読み取りと書き込み、ネットワーク通信、その他の操作が頻繁に必要になります。ここでは、システム ファイルの頻繁な読み取りと書き込みを使用して、I/O 集中型の同時タスクの処理をシミュレートします。
- 並行関数を定義し、内部で I/O 機能を集中的に呼び出します。
import fs from '@ohos.file.fs';
// 定义并发函数,内部密集调用I/O能力
@Concurrent
async function concurrentTest(fileList: string[]) {
// 写入文件的实现
async function write(data, filePath) {
let file = await fs.open(filePath, fs.OpenMode.READ_WRITE);
await fs.write(file.fd, data);
fs.close(file);
}
// 循环写文件操作
for (let i = 0; i < fileList.length; i++) {
write('Hello World!', fileList[i]).then(() => {
console.info(`Succeeded in writing the file. FileList: ${fileList[i]}`);
}).catch((err) => {
console.error(`Failed to write the file. Code is ${err.code}, message is ${err.message}`)
return false;
})
}
return true;
}
2. TaskPool を使用して、集中的な I/O を含む同時関数を実行します。execute()メソッド を呼び出してタスクを実行し、コールバックでスケジュール結果を処理します。この例で filePath1 と filePath2 を取得する方法については、「アプリケーション ファイル パスの取得」を参照してください。
import taskpool from '@ohos.taskpool';
let filePath1 = ...; // 应用文件路径
let filePath2 = ...;
// 使用TaskPool执行包含密集I/O的并发函数
// 数组较大时,I/O密集型任务任务分发也会抢占主线程,需要使用多线程能力
taskpool.execute(concurrentTest, [filePath1, filePath2]).then((ret) => {
// 调度结果处理
console.info(`The result: ${ret}`);
})