跳转至

在 SOUI 中使用异步任务

在客户端应用程序开发中,我们经常需要执行耗时操作(如网络请求、文件读写等),这些操作如果在 UI 线程中执行会导致界面卡顿。SOUI 提供了多种异步任务处理机制,帮助开发者将耗时操作放到后台线程执行,同时安全地更新 UI。

异步任务处理方式

SOUI 提供了两种主要的异步任务处理方式:

  1. TaskLoop 组件:独立的异步任务处理模块
  2. IMessageLoop 接口:基于消息循环的异步任务处理

使用 IMessageLoop 处理异步任务

IMessageLoop 是 SOUI 中处理异步任务的核心接口,它允许我们将任务提交到 UI 线程的消息循环中执行。

IMessageLoop 接口定义

SNSBEGIN
    DECLARE_INTERFACE_(IMessageLoop, IObjRef){
    /**
     * @brief 提交一个任务到消息循环
     * @param runable 可运行对象指针
     * @return 成功返回 TRUE,失败返回 FALSE
     */
    STDMETHOD_(BOOL, PostTask)(THIS_ IRunnable * runable) PURE;

    /**
     * @brief 删除与指定对象关联的所有任务
     * @param pObj 对象指针
     * @return 删除的任务数量
     */
    STDMETHOD_(int, RemoveTasksForObject)(THIS_ void *pObj) PURE;
    };
SNSEND

核心方法说明

  1. PostTask:创建并提交一个任务到消息循环中。这个任务会在消息循环的线程中执行。
  2. RemoveTasksForObject:删除指定对象关联的所有任务。当一个对象释放后,必须调用此方法删除关联的任务,否则可能导致内存泄漏。

使用 IMessageLoop 的优势

当工作线程完成任务需要 UI 线程刷新界面时,可以使用 PostTask 方法提交任务到消息循环中。这样,当 UI 线程处理完当前任务时,会自动执行 PostTask 方法中的任务,确保任务在 UI 线程中执行,从而避免 UI 线程被阻塞。

TaskHelper 辅助类

SOUI 提供了 TaskHelper 类来简化异步任务处理。TaskHelper 类提供了一些静态方法,用于创建和提交异步任务。

使用示例

#include <helper/STaskHelper.h>

class CMyTaskObj
{
    SAutoRefPtr<IMessageLoop> m_pMsgLoop;

public:
    CMyTaskObj(){
        // 在 UI 线程中创建,获取 UI 线程的 MsgLoop
        m_pMsgLoop = SApplication::getSingleton().GetMsgLoop();
    }

    ~CMyTaskObj(){
        // 删除所有任务
        m_pMsgLoop->RemoveTasksForObject(this);
    }

    void task1(int a)
    {
        SLOG_INFO("task1,a:" << a);
    }

    void task2(int a, const std::string & b)
    {
        SLOG_INFO("task2,a:" << a<<" b:"<<b.c_str());
    }

    void thread_func(){
        // 线程函数
        while(true){
            // 将一个任务提交到 UI 线程
            STaskHelper::post(m_pMsgLoop, this, &CMyTaskObj::task1, 100, true);

            // 将一个任务提交到 UI 线程
            STaskHelper::post(m_pMsgLoop, this, &CMyTaskObj::task2, 100, "abc", true);

            // 将一个 Lambda 表达式提交到 UI 线程
            STaskHelper::post(m_pMsgLoop, &StdRunnable([=]{
                // 在 UI 线程中执行
                // 注意:lambda 表达式创建的 Runnable 对象由于没有 this 指针标志,
                // 因此必须保证线程的生命周期在 this 生命周期内,否则可能导致程序崩溃
                SLOG_INFO("task3");
            }));
        }
    }
};

使用建议

IMessageLoop vs NotifyCenter

使用 IMessageLoop 来提交异步 UI 任务相对于 NotifyCenter 有以下优势:

  1. 使用更简单:直接通过 PostTask 方法提交任务
  2. 参数传递灵活:相对于传统的 PostMessage,STaskHelper::post 可以传递任意类型的参数,SOUI 自动完成参数解析,代码可读性更高

注意事项

使用 STaskHelper::post 时需要注意以下几点:

  1. 参数顺序:参数的顺序必须与函数的参数顺序一致
  2. 参数拷贝:参数的值会被拷贝一份,因此引用类型参数不能使用
  3. 指针参数:指针类型参数可能需要使用 std::shared_ptr 等智能指针,以确保参数在函数调用结束后不会被释放
  4. 生命周期管理:特别注意 Lambda 表达式创建的 Runnable 对象,需要确保相关对象的生命周期

TaskLoop 组件

除了使用 IMessageLoop 处理异步任务外,SOUI 还提供了 TaskLoop 组件,这是一个独立的异步任务处理模块,特别适用于需要大量后台任务处理的场景。

TaskLoop 的核心接口包括:

  1. IRunnable:任务执行的基本接口
  2. ITaskLoop:任务循环的核心接口

TaskLoop 支持任务优先级管理,可以设置任务的优先级(高、普通、低),任务会根据优先级自动排序执行。

更多关于 TaskLoop 的详细信息,请参考相关文档。