在 SOUI 中使用异步任务¶
在客户端应用程序开发中,我们经常需要执行耗时操作(如网络请求、文件读写等),这些操作如果在 UI 线程中执行会导致界面卡顿。SOUI 提供了多种异步任务处理机制,帮助开发者将耗时操作放到后台线程执行,同时安全地更新 UI。
异步任务处理方式¶
SOUI 提供了两种主要的异步任务处理方式:
- TaskLoop 组件:独立的异步任务处理模块
- 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
核心方法说明¶
- PostTask:创建并提交一个任务到消息循环中。这个任务会在消息循环的线程中执行。
- 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 有以下优势:
- 使用更简单:直接通过 PostTask 方法提交任务
- 参数传递灵活:相对于传统的 PostMessage,STaskHelper::post 可以传递任意类型的参数,SOUI 自动完成参数解析,代码可读性更高
注意事项¶
使用 STaskHelper::post 时需要注意以下几点:
- 参数顺序:参数的顺序必须与函数的参数顺序一致
- 参数拷贝:参数的值会被拷贝一份,因此引用类型参数不能使用
- 指针参数:指针类型参数可能需要使用 std::shared_ptr 等智能指针,以确保参数在函数调用结束后不会被释放
- 生命周期管理:特别注意 Lambda 表达式创建的 Runnable 对象,需要确保相关对象的生命周期
TaskLoop 组件¶
除了使用 IMessageLoop 处理异步任务外,SOUI 还提供了 TaskLoop 组件,这是一个独立的异步任务处理模块,特别适用于需要大量后台任务处理的场景。
TaskLoop 的核心接口包括:
- IRunnable:任务执行的基本接口
- ITaskLoop:任务循环的核心接口
TaskLoop 支持任务优先级管理,可以设置任务的优先级(高、普通、低),任务会根据优先级自动排序执行。
更多关于 TaskLoop 的详细信息,请参考相关文档。