ArkTs-线程池工具封装

写在前面

taskpool工具进行封装,将常用api及异步锁封装进去,减少使用的复杂度。


使用示例

复制代码
// 工具类使用
ThreadUtils.runOnTaskPoolV2({
  taskName: "task_name",
  func: realConcurrentFunc,
  args: [context,
      ...
      ],
  asyncLockKey: 'async_lock_key',
  uiThreadReceiveFunc: uiThreadCallbackFunc,
  sequenceKey: "taskpool_sequence_key"
});


// 实际执行的方法  
@Concurrent
export async function realConcurrentFunc(context: Context,...) {
    ...
}


// 主线程回调方法
function uiThreadCallbackFunc(...) {
    ...
}

参数解析

参数名 类型 含义 默认值 是否必须 注意事项
taskName string 任务名,创建task时传入的变量 '' 非必须传
func Function 真正执行的函数 无默认值 1.必须传,不可为空2.必须为@Concurrent
args Object[] func所需的参数, [] 否,根据func决定 1.参数必须为Sendable2.此处的参数用数组的方式传进来,真正调用func时会将参数一个个传进去。例如:入参为[1, 'hello', context]执行时为func(1, 'hello', context)
asyncLockKey string 异步锁的key '' 1.非必须传,如果不传或传空字符串,则不适用异步锁2.异步锁根据key来管理,需要用到同一个异步锁的传相同的key即可
uiThreadReceiveFunc Function 主线程回调函数 undefined 1.非必须传,需要回调主线程时使用2.调用方式是在func中通过以下方式实现taskpool.Task.sendData(...args)
sequenceKey string task顺序执行的key '' 1.非必须传,需要子线程任务串行执行时使用2.负责执行task的runner,根据sequenceKey来选择;如无执行顺序要求,建议不传或传空字符串

流程图

代码解析

工具类方法封装

1.对传入的参数进行解析,添加默认值

2.因为可以选择是否使用异步锁,封装了一个壳方法进行加锁

3.根据用户选择的使用需要串行执行,选择taskpool or runner

复制代码
    /**
     * 切换至工作线程执行
     *
     * api使用限制:
     * 1.在工作线程执行的函数,必须有@Concurrent注解
     * 2.在工作线程执行的函数,参数必须为可序列化的
     *
     * 如果待执行函数中需要向host线程回调,在task的func中,通过task.sendData(...args: Object[])向host线程发送数据,
     * args为calback所需要的参数。
     *
     * @param taskName 任务名称
     * @param func 子线程待执行方法
     * @param args 子线程待执行方法的参数
     * @param uiThreadReceiveFunc 主线程回调函数
     * @returns
     */
    public static async runOnTaskPoolV2(runOnTaskpoolData: RunOnTaskpoolData): Promise<Object | null | undefined> {
        // 参数校验
        const taskName: string = runOnTaskpoolData.taskName ?? '';
        const func: Function = runOnTaskpoolData.func;
        const args: Object[] = runOnTaskpoolData.args ?? [];
        const asyncLockKey: string = runOnTaskpoolData.asyncLockKey ?? '';
        const uiThreadReceiveFunc: Function | undefined = runOnTaskpoolData.uiThreadReceiveFunc;
        const sequenceKey: string = runOnTaskpoolData.sequenceKey ?? '';

        // 异步锁
        let asyncLock: ArkTSUtils.locks.AsyncLock | null = AsyncLockManager.getInstance().getAsyncLock(asyncLockKey);

        // 构建task
        let bgTask = new taskpool.Task(taskName, shellFunction, [func, args, asyncLock]);

        // 回调函数
        if (uiThreadReceiveFunc) {
            bgTask.onReceiveData(uiThreadReceiveFunc);
        }

        // 判断是否需要顺序执行
        if (StringUtils.isNotEmpty(sequenceKey)) {
            // 需要顺序执行,使用sequenceRunner
            let runner: taskpool.SequenceRunner | undefined = BMThreadUtils.sequenceRunnerRecord[sequenceKey];
            if (!runner) {
                runner = new taskpool.SequenceRunner();
                BMThreadUtils.sequenceRunnerRecord[sequenceKey] = runner;
            }
            return await runner.execute(bgTask);
        } else {
            // 不需要顺序执行,使用普通task
            return await taskpool.execute(bgTask);
        }
    }

壳方法

工具类默认执行的方法,进行异步锁加锁处理后,执行真正的函数func,同时将args从数组参数展开传递给函数

复制代码
/**
 * taskpool中切换至工作线程执行的壳function
 * 1.为方法执行加锁
 *
 * tips:
 *
 * taskpool执行的方法、参数有如下要求:(必须满足,否则执行时会报错)
 * 1.taskpool中传入的function,必须携带@Concurrent注解
 * 2.taskpool中传入的function,参数必须为可序列化的
 *
 * 如果你的方法中需要使用到【调用方】的方法、变量,无法在调用方中声明该方法,会爆出如下错误:
 * "Only imported variablies and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>"
 *
 * 系统组件提供了一些可序列化的方法,涉及到的尽量使用系统api:
 * 1.共享用户首选项
 * 2.可共享的色彩管理
 * 3.基于Sendable对象的图片处理
 * 4.资源管理
 * 5.SendableContext对象管理
 */
@Concurrent
export async function shellFunction(shellFuncArgs: Object[]): Promise<Object | null | undefined> {
    try {
        // 入参
        const func: Function = shellFuncArgs[0] as Function;
        const args: Object[] = shellFuncArgs[1] as Object[];
        const asyncLock: ArkTSUtils.locks.AsyncLock | null = shellFuncArgs[2] as ArkTSUtils.locks.AsyncLock | null;

        // 实际执行的函数
        const executeLogic = async () => {
            return await func(...args);
        };

        // 如果有锁则加锁执行
        if (asyncLock) {
            return await asyncLock.lockAsync(async () => {
                return await executeLogic();
            });
        } else {
            return await executeLogic();
        }
    } catch (error) {
        BMLog.error('ConcurrentFunction', `shellFunction error: code=${error.code}, message=${error.message}`);
    }
    return null;
}

异步锁管理

根据key来管理异步锁,互相影响的任务可传入相同的key,来保证执行顺序

复制代码
/**
 * 异步锁管理器
 *
 * 提供线程池使用的异步锁,保存在主线程中;在主线程调用时获取到,传入子线程使用
 */
export class AsyncLockManager {
    /**
     * 默认线程池的key
     */
    public static readonly KEY_TASKPOOL_DEFAULT: string = "taskPoolDefault";

    /**
     * 线程池异步锁
     */
    public asyncLockRecord: Record<string, ArkTSUtils.locks.AsyncLock> = {};

    private static instance: AsyncLockManager;

    private constructor() {}

    public static getInstance(): AsyncLockManager {
        if (!AsyncLockManager.instance) {
            AsyncLockManager.instance = new AsyncLockManager();
        }
        return AsyncLockManager.instance;
    }

    /**
     * 获取异步锁,当key穿空时返回null
     * @param key 锁的key
     * @returns 异步锁
     */
    public getAsyncLock(key: string = AsyncLockManager.KEY_TASKPOOL_DEFAULT): ArkTSUtils.locks.AsyncLock | null {
        if (BMStringUtils.isEmpty(key)) {
            return null;
        }
        if (!this.asyncLockRecord[key]) {
            this.asyncLockRecord[key] = new ArkTSUtils.locks.AsyncLock();
        }
        return this.asyncLockRecord[key];
    }
}

task执行顺序

taskpool提供了保证task顺序执行的api,需要保证执行顺序的api可传入相同的key

复制代码
    /**
     * 顺序执行任务taskpool
     */
    private static sequenceRunnerRecord: Record<string, taskpool.SequenceRunner> = {};
    
    
        // 判断是否需要顺序执行
        if (BMStringUtils.isNotEmpty(sequenceKey)) {
            // 需要顺序执行,使用sequenceRunner
            let runner: taskpool.SequenceRunner | undefined = BMThreadUtils.sequenceRunnerRecord[sequenceKey];
            if (!runner) {
                runner = new taskpool.SequenceRunner();
                BMThreadUtils.sequenceRunnerRecord[sequenceKey] = runner;
            }
            return await runner.execute(bgTask);
        } else {
            // 不需要顺序执行,使用普通task
            return await taskpool.execute(bgTask);
        }

写在后面

If you like this article, it is written by Johnny Deng.

If not, I don't know who wrote it.

相关推荐
后端小张8 小时前
【JAVA进阶】鸿蒙开发与SpringBoot深度融合:从接口设计到服务部署全解析
java·spring boot·spring·spring cloud·华为·harmonyos·鸿蒙
威哥爱编程16 小时前
【鸿蒙开发案例篇】鸿蒙6.0的pdfService与pdfViewManager终极爆破
harmonyos·arkts·arkui
kirk_wang1 天前
Flutter 三方库在 OHOS 平台的适配实践:以 flutter_mailer 为例
flutter·移动开发·跨平台·arkts·鸿蒙
江澎涌1 天前
JWorker——一套简单易用的基于鸿蒙 Worker 的双向 RPC 通讯机制
typescript·harmonyos·arkts
威哥爱编程2 天前
【鸿蒙开发案例篇】定点出击!鸿蒙6.0视频碰一碰流转+实时进度同步案例
harmonyos·arkts·arkui
kirk_wang2 天前
Flutter media_info插件在OpenHarmony平台的适配实践
flutter·移动开发·跨平台·arkts·鸿蒙
威哥爱编程3 天前
【鸿蒙开发案例篇】拒绝裸奔!鸿蒙6实现PDF动态加密
harmonyos·arkts·arkui
Simon席玉3 天前
C++的命名重整
开发语言·c++·华为·harmonyos·arkts
遇到困难睡大觉哈哈3 天前
Harmony os —— Data Augmentation Kit 知识问答实战全流程(流式 RAG 问答踩坑记录)
harmonyos·鸿蒙