timer API 文档
概述
mc::timer 是基于 mc::runtime 执行器的定时器对象,提供周期触发与单次触发能力,
并通过与 mc::object 父子体系配合的生命周期契约,保证 stop() 之后调用方智能指针
reset() 即确定释放对象。
主要特性
- 周期定时与单次触发(
single_shot),到期回调timeout信号分发到宿主 executor 执行 - 生命周期契约:父对象强持有只覆盖"构造/set_parent 起、下次 stop 前"以及每次运行期;stop 后显式 set_parent 会重新挂链(基类语义);
stop()后摘除强持有,reset()确定释放;single_shot触发完毕自动摘链不累积 - 摘链只移除强持有,弱血缘保留:
get_parent()与executor()的父链解析不受影响 stop()排空排队中的回调后才返回,可安全地在回调线程内调用single_shot支持以对象方法、std::function、宿主 context 三种形式注册,回调经 weak 引用保护,宿主先析构则静默取消- 无 parent 的
single_shot必须保留返回的timer_ptr直到回调完成;传入 parent 时由 parent 在等待期间强持有,调用方可以使用 fire-and-forget 形式
父锚点:以 interface 作为 parent
mc::engine::interface(设备对象内的值成员接口)本身不受引用计数管理,直接作为
parent 时弱父链无法建立(weak_from_this() 为空)。自本版本起,装配期
interface_impl::set_owner() 会同步写入父锚点(object::set_parent_anchor),
set_parent() 归一化时将传入的接口对象替换为其宿主设备对象再挂链:
- 以接口方法内的
this创建 timer,实际挂靠宿主设备对象(锚点仅跟随一跳,不支持嵌套/成环),stop()摘链、 executor 沿父链解析均经宿主生效——业务代码无需区分this与get_owner(); - 宿主未设置(独立接口)或宿主非引用管理对象/已失效时,保持传入对象为 parent
(旧语义):fire-and-forget
single_shot的兜底保活行为不变; - 挂链语义仅"从接口改为宿主"这一处变化,children 归属随宿主,随宿主拆机统一回收。
void NetworkPort::start_poll() {
// 接口方法内直接用 this:timer 实际挂靠宿主设备对象
m_timer = mc::make_shared<mc::timer>(this);
m_timer->timeout.connect([this]() { poll(); });
m_timer->set_single_shot(false);
m_timer->start(mc::milliseconds(10000));
}
void NetworkPort::stop_poll() {
// 统一契约:stop() 摘除宿主持有,reset() 确定释放
if (m_timer) { m_timer->stop(); m_timer.reset(); }
}
基本用法
例 1:周期定时器,stop 后 reset 确定释放
#include <mc/object.h>
#include <mc/runtime.h>
#include <mc/timer.h>
void periodic_sample(mc::object* host)
{
auto t = mc::make_shared<mc::timer>(host);
t->set_interval(mc::milliseconds(500));
t->timeout.connect([]() {
// 每 500ms 在宿主 executor 上执行一次
});
t->start();
// 停止后 timer 从父对象摘除,生命周期归还调用方:
// reset() 即确定释放,不会等到宿主析构才回收
t->stop();
t.reset();
}
例 2:single_shot 一次性触发(context 兜底保活)
#include <mc/timer.h>
void one_shot_sample(mc::object* context)
{
// 返回值可以不持有:等待触发期间由 context 强持有兜底,
// 触发完毕后自动摘链,不在 context 上累积
mc::timer::single_shot(mc::milliseconds(1000), context, []() {
// 1 秒后执行一次
});
}
无 parent 的单次定时器没有父对象提供强持有,调用方必须保留返回值:
mc::timer_ptr start_one_shot()
{
auto timer = mc::timer::single_shot(mc::milliseconds(1000), []() {
// 1 秒后执行一次
});
return timer; // 持有至回调完成
}
例 3:成员方法回调与运行期重启
#include <mc/timer.h>
class sampler : public mc::object, public mc::enable_shared_from_this<sampler> {
public:
explicit sampler(mc::object* parent) : mc::object(parent), m_timer(mc::make_shared<mc::timer>(this))
{
m_timer->timeout.connect([self = weak_from_this()]() {
if (auto ptr = self.lock()) {
static_cast<sampler*>(ptr.get())->on_tick();
}
});
}
void begin()
{
m_timer->start(mc::milliseconds(1000)); // start 重挂父持有,运行期由父对象兜底
}
void suspend()
{
m_timer->stop(); // 摘链;m_timer 成员仍持有,对象不销毁
}
private:
void on_tick() {}
mc::timer_ptr m_timer;
};
API 详细说明
类 mc::timer(继承 mc::object)
| 方法 | 参数 | 返回值 | 异常 | 线程安全 | 说明 |
|---|---|---|---|---|---|
| timer | object* parent = nullptr |
无 | 无(不抛出) | 否(构造期单线程) | 按基类契约立即挂入父对象强持有 |
| ~timer | 无 | 无 | 无(不抛出) | 否 | 停止定时并释放资源 |
| interval | 无 | mc::milliseconds |
无(不抛出) | 与 start/stop 并发需外部同步 | 读取定时器间隔 |
| set_interval | mc::milliseconds msec |
void | 无(不抛出) | 与 start/stop 并发需外部同步 | 设置间隔;若定时器运行中立即按新间隔重启 |
| is_single_shot | 无 | bool |
无(不抛出) | 与 set_single_shot 并发需外部同步 | 是否只执行一次 |
| set_single_shot | bool single_shot |
void | 无(不抛出) | 与 is_single_shot 并发需外部同步 | 设置是否只执行一次 |
| is_active | 无 | bool |
无(不抛出) | 是(原子读) | 定时器是否处于运行状态 |
| start | mc::milliseconds msec = 0 |
void | 无(不抛出) | 与 set_interval 并发需外部同步 | 启动/重启定时器;重新挂入父对象强持有;已启动则按新间隔重启 |
| stop | 无 | void | 无(不抛出) | 是(可在回调线程内调用,同线程不等待) | 停止触发并排空排队回调后,从父对象摘除强持有(保留弱血缘),生命周期归还调用方 |
| timeout | 信号 mc::signal<void()> |
— | — | 槽执行在宿主 executor | 到期信号,回调派发到宿主 executor 执行 |
静态函数
template <typename Object, typename BaseObject = Object>
static timer_ptr single_shot(mc::milliseconds msec, mc::shared_ptr<Object>& receiver,
void (BaseObject::*method)());
template <typename Object, typename BaseObject = Object>
static timer_ptr single_shot(mc::milliseconds msec, Object* receiver,
void (BaseObject::*method)());
static timer_ptr single_shot(mc::milliseconds msec, std::function<void()> functor);
template <typename Object>
static timer_ptr single_shot(mc::milliseconds msec, mc::shared_ptr<Object>& context,
std::function<void()> functor);
static timer_ptr single_shot(mc::milliseconds msec, object* context, std::function<void()> functor);
| 参数 | 说明 |
|---|---|
| msec | 触发延迟 |
| receiver / method | 以对象成员方法为回调;内部持 weak 引用,宿主先析构则静默取消 |
| context | 宿主对象:解析回调 executor,等待触发期间强持有兜底,触发完毕自动摘链;不提供 context 时需由调用方保留返回的 timer_ptr |
| functor | 回调可调用对象;为空时直接返回空指针 |
返回 timer_ptr:提供 context 时调用方可以不持有;未提供 context 时需持有返回值直到回调完成;functor 为空时返回空 timer_ptr。
最佳实践
✅ 正例:stop 后立即 reset 释放
m_timer->stop();
m_timer.reset(); // stop 已摘除父持有,此处确定释放对象
✅ 正例:fire-and-forget 的 single_shot 指定 context
// context 负责保活与 executor 解析;触发后自动摘链,长生命周期对象上不会累积
mc::timer::single_shot(mc::milliseconds(200), this, [this]() { retry(); });
❌ 反例:只 reset 不 stop 就期望对象释放
m_timer.reset(); // 运行期由父对象兜底保活,对象不会被释放,回调继续触发
❌ 反例:依赖"停止的 timer 仍在父对象 children 中"做遍历
// stop 后 timer 已从 children 摘除,find_child 找不到已停止的 timer;
// 需要持有 timer_ptr 自行管理
auto t = parent->find_child("poll");
❌ 反例:把 timer 当普通 object 期待永久父子挂载
// 周期性 start/stop 会让对象随运行状态进出父对象 children 列表,
// 依赖固定父子树的代码(如按 children 枚举做统一注销)不应把 timer 计入