You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以线程安全方式取消Solace上下文注册的定时器并释放状态对象?

安全实现Solace定时器关联C++对象并跨线程取消释放的方案

我来帮你梳理下这个场景下的安全实现方案——核心是要处理好线程同步、定时器ID的生命周期以及C++对象的安全释放这几个关键点,具体可以按以下步骤来:

1. 给定时器绑定你的C++状态对象

Solace的solClient_context_startTimer接口本身就支持通过最后一个void* user_p参数传递用户自定义数据,直接把你的C++对象指针传进去就行,回调里再强转回来使用:

// 假设你的C++状态类是这样的
class MyTimerState {
public:
    // 你的状态数据和方法...
};

// 定时器回调函数
void myTimerCallback(solClient_timerId_t timerId, void* user_p) {
    // 把void*强转回你的C++对象指针
    MyTimerState* state = static_cast<MyTimerState*>(user_p);
    // 这里执行你的定时器逻辑,比如访问state里的状态数据
}

// 启动定时器时绑定对象
void startMyTimer(solClient_context_pt context_p) {
    MyTimerState* timerState = new MyTimerState();
    solClient_timerId_t timerId;
    solClient_returnCode_t rc = solClient_context_startTimer(
        context_p,
        1000,  // 定时间隔(毫秒),如果是一次性定时器,触发后会自动取消
        myTimerCallback,
        timerState,  // 传递你的C++对象指针
        &timerId
    );

    if (rc == SOLCLIENT_OK) {
        // 这里需要把timerId和对应的state对象存起来,供后续取消使用
        // 具体存储方式看下面的线程安全部分
    } else {
        // 启动失败,提前释放对象避免内存泄漏
        delete timerState;
    }
}

2. 线程安全地管理定时器ID与状态对象映射

因为你要从其他线程取消定时器,必须保证对定时器ID和状态对象的访问是线程安全的。可以用一个带互斥锁保护的哈希表来维护它们的映射关系:

#include <mutex>
#include <unordered_map>

// 全局或类成员的线程安全映射表
std::mutex timerMapMutex;
std::unordered_map<solClient_timerId_t, MyTimerState*> timerIdToStateMap;

// 修改startMyTimer里的成功分支:
if (rc == SOLCLIENT_OK) {
    std::lock_guard<std::mutex> lock(timerMapMutex);
    timerIdToStateMap[timerId] = timerState;
}

3. 跨线程取消定时器并安全释放对象

在其他线程调用取消接口时,必须先加锁取出对应的状态对象,取消定时器后再释放对象,同时从映射表中移除条目,避免重复操作:

void cancelMyTimer(solClient_context_pt context_p, solClient_timerId_t timerId) {
    std::lock_guard<std::mutex> lock(timerMapMutex);
    auto it = timerIdToStateMap.find(timerId);
    if (it != timerIdToStateMap.end()) {
        MyTimerState* state = it->second;
        // 取消定时器
        solClient_returnCode_t rc = solClient_context_cancelTimer(context_p, timerId);
        
        if (rc == SOLCLIENT_OK) {
            // 取消成功,安全释放C++对象
            delete state;
        } else {
            // 取消失败的情况:比如定时器已经触发并执行了回调
            // 这时候需要确认回调是否已经处理了对象的释放(看下面回调的处理逻辑)
            // 一般来说,回调里如果是一次性定时器,会自己移除映射并释放对象
            // 所以这里不需要重复释放,避免野指针
        }
        
        // 不管取消成功与否,都从映射表中移除该条目,防止后续重复操作
        timerIdToStateMap.erase(it);
    }
}

4. 定时器回调里的安全处理

因为定时器回调可能和取消操作并发执行,所以回调里也要加锁,确保状态对象的访问和释放是安全的:

void myTimerCallback(solClient_timerId_t timerId, void* user_p) {
    MyTimerState* state = static_cast<MyTimerState*>(user_p);
    std::lock_guard<std::mutex> lock(timerMapMutex);
    
    auto it = timerIdToStateMap.find(timerId);
    if (it != timerIdToStateMap.end()) {
        // 执行你的定时器业务逻辑...
        
        // 如果是**一次性定时器**,触发后需要手动取消并释放对象
        solClient_context_cancelTimer(context_p, timerId);
        delete state;
        timerIdToStateMap.erase(it);
    }
    // 如果是**周期性定时器**,不要在这里释放对象,除非你要停止定时器
}

关键注意事项

  • Solace Context的线程安全性:Solace的context对象本身是线程安全的,所以你可以从不同线程调用solClient_context_startTimer和solClient_context_cancelTimer,不需要额外加锁保护context。
  • 避免悬垂指针:一定要保证在释放C++对象前,定时器已经被取消,或者回调里不会再访问已经释放的对象——互斥锁的作用就是保证这一点。
  • 内存泄漏检查:如果启动定时器失败,一定要提前释放创建的状态对象;如果取消失败,要确认回调是否已经处理了对象的释放,避免重复释放或泄漏。

内容的提问来源于stack exchange,提问作者Steve Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:37:25