/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
目录
-
- 摘要
- 1. 架构与文件构成
- 2. TimerTask
- 3. bug1:构造顺序
- 4. bug2:TimerAdd 与 Refresh
- 5. bug5:TickRun
- 6. bug3、bug4:两套索引
- 7. 总结
- 8. 常见问题
- 9. 标签
# timewheel.cpp 源码走查:TimerTask 与 TimeWheel
摘要
单文件时间轮:TimerTask 持 id、超时与回调,析构中执行任务;TimeWheel 用 _timers 与 _wheel 两套索引,外部每秒 TickRun 驱动。
1. 架构与文件构成
两个类:TimerTask、TimeWheel。_timers 管 id,_wheel 管时间位置。main、TimerAdd、每秒 TickRun;到期时 clear() 释放该位置 PtrTask,无其他强引用则 ~TimerTask 执行任务,_release 回调 RemoveTimer。
【流程图占位:全链路;节点:main 循环 sleep → TickRun → (_tick+1)%_capacity → clear → ~TimerTask → RemoveTimer】
2. TimerTask
_cancel 决定两处:析构中为 true 时提前 return,回调均不执行;SetRealese 拒绝设置。SetCancel 状态未变时返回 false。
摘录(timewheel.cpp,TimerTask::SetCancel):
bool
SetCancel (bool b)
{
if (b == _cancel)
{
spdlog::info ("SetCancel error 已经set过");
return false;
}
_cancel = b;
return true;
}
3. bug1:构造顺序
声明序 _tick、_capacity、_timers、_wheel;构造列表 _wheel(_capacity) 依赖 _capacity 先声明。
摘录(timewheel.cpp,TimeWheel 私有成员):
private:
int _tick; // 滴答下标
uint64_t _capacity; // 表盘最大数量—其实就是最大延迟时间
// Hash-通过id进行管理 blog分析🔍销毁(shared_ptr与weak_ptr)
std::unordered_map<uint64_t, WeakTask>
_timers; // <id,weak_ptr> 管理销毁与创建
std::vector<std::vector<PtrTask>>
_wheel; // 二维时间轮
【bug1】:未顾及初始化顺序,错误的以“读顺序优先”
4. bug2:TimerAdd 与 Refresh
TimerAdd:查 _timers 去重,SetRealese 绑 RemoveTimer+this+id,插入 _wheel[delay]。Refresh:pos = 超时 + _tick 取模 _capacity,再头插同一 PtrTask。
摘录(timewheel.cpp,TimeWheel::TimerAdd):
pt->SetRealese (std::bind (&TimeWheel::RemoveTimer, this, id));
// _wheel[delay].push_back (pt);
_wheel[delay].insert (_wheel[delay].begin (), pt);
【bug2】:对结构不熟悉,写代码纯粹的不细心。
5. bug5:TickRun
TickRun:遍历 _wheel[(_tick + 1) % _capacity],逐个 SetCancel(false);返回 true 记 flag–,flag < 0 时打印并 return,_tick 不前进;否则 _tick 前进一格并 clear()。
摘录(timewheel.cpp,TimeWheel::TickRun):
for (auto &e : _wheel[(_tick + 1) % _capacity])
{
// if (!e->SetCancel (true))可以吗? bug5 不可以!!
if (e->SetCancel (false))
【bug5】:想要复用逻辑,复用仅存在一种有效方法
【流程图占位:TickRun 单次推进;节点:定下标 (_tick+1)%_capacity → 遍历 SetCancel → 失败计数 → _tick 前进 → clear()】
6. bug3、bug4:两套索引
RemoveTimer 经 _release 调用:find 失败打印 remove error,成功则 erase(id)。SetCanCel:lock() 取 GetTimeOut() 得 pos,再遍历 _wheel[pos] 逐个 SetCancel(true)。
摘录(timewheel.cpp,TimeWheel::RemoveTimer / SetCanCel):
auto it = _timers.find (id);
if (it == _timers.end ())
{
spdlog::error ("remove error");
return;
}
uint32_t pos = _timers[id].lock ()->GetTimeOut ();
【bug3】 【bug4】
7. 总结
结构:任务状态 + 两套索引 + 外部驱动;
8. 常见问题
9. 标签
时间轮 / C++ / 智能指针 / weak_ptr
网硕互联帮助中心


评论前必须登录!
注册