RussellLuo / timingwheel

Golang implementation of Hierarchical Timing Wheels.
MIT License
656 stars 121 forks source link

增加 `func (tw *TimingWheel) EveryFunc(d time.Duration, f func()) *Timer` 方法 #8

Closed Allenxuxu closed 4 years ago

Allenxuxu commented 5 years ago

大佬,考虑增加 定时任务 的接口吗

Pull requests : https://github.com/RussellLuo/timingwheel/pull/9

RussellLuo commented 4 years ago

@Allenxuxu 谢谢关注!

基本原则

我希望这个库能够做到最简化:只提供最基本的、必不可少的 API(功能)。不同的应用场景(use cases),可以通过组合使用基本功能来达成目标。

解决思路

这里提到的想要实现 “定时任务”,可以用 AfterFunc 来模拟:即每次执行 f 后,再把 f 重新加入时间轮。

具体有两种实现方式:

1. 每次创建新的 Timer

// tw := timingwheel.NewTimingWheel(...)
// tw.Start()
// defer tw.Stop()

var f fun()
f = func() {
    // main handling logic
    // ...

    tw.AfterFunc(time.Second, f)
}
tw.AfterFunc(time.Second, f)

2. 复用已有的 Timer

第 1 种方式的问题是,会增加 GC 的负担:因为每次重复执行任务时,会创建一个新的 Timer,老的 Timer 等待 GC 回收。

有两种方式可以复用已有的 Timer:

a) 新增一个特定 API

即针对 “定时任务” 的特殊场景,新增一个专门的 API(就是你的 PR 里的 EveryFunc)。但是这种特殊的 API 只能用于 “定时任务”,别的应用场景无法复用,所以违背了上述「基本原则」。

b) 新增一个通用 API

有一种满足「基本原则」的方式,是新增一个 Reset 方法(类似 time 标准库的 Timer.Reset ),任何需要重置 timer 的地方都可以使用。

如果新增了 Reset 方法,“定时任务” 的实现代码变为:

// tw := timingwheel.NewTimingWheel(...)
// tw.Start()
// defer tw.Stop()

var t *timingwheel.Timer
t = tw.AfterFunc(time.Second, func() {
    // main handling logic
    // ...

    // Reset t
})

这里写成 //Reset t 形式,是因为 Reset 的具体实现方式还没有确定。欢迎在这里一起讨论:https://github.com/RussellLuo/timingwheel/issues/10

结论

@Allenxuxu 你的建议呢?

Allenxuxu commented 4 years ago

@RussellLuo 感谢🙏大佬如此深入的回答!

对于 方案 1 每次创建新的 Timer ,这样肯定是不能用来实现”定时任务“的,因为无法通过返回 timer 去关闭定时器(使用者通过封装也是可以的,但是太繁琐了)

方案 2 的 b 实现 Timer.Reset ,我认为很好,通过简单的封装,可以实现”定时任务“ 。但是既然使用者需要“定时任务”接口,为什么还要他每次使用都去复制粘贴一遍呢。库中来封装好它,也不会增加复杂度,还可以提高易用性。

个人建议,Timer.Reset 接口需要,“定时任务”接口也可以封装好提供。

RussellLuo commented 4 years ago

但是既然使用者需要“定时任务”接口,为什么还要他每次使用都去复制粘贴一遍呢。库中来封装好它,也不会增加复杂度,还可以提高易用性。

你说得有道理 👍

这个库之前没有考虑过要支持 “定时任务” 的概念,所以我想的是增加 Reset 接口,可以顺便辅助实现 “定时任务”;如果这个库的定位本身就需要支持 “定时任务”,那增加一个专门的 API 无疑是最佳选择。

至于 “timingwheel 是否要支持定时任务,以及应该以怎样的形式支持“,最近比较忙,给我点时间再考虑考虑 :-)

RussellLuo commented 4 years ago

@Allenxuxu 我这两天想了下。“定时任务” 本质上是重复性任务(repetitive tasks),而上面讨论的 “每隔固定 interval 执行的任务” 只是重复性任务的其中一种形式。

所以,我考虑在 timingwheel 中增加一种 支持重复性任务的通用机制,从而为实现各种形式的 “定时任务” 提供可能。

详情参考 https://github.com/RussellLuo/timingwheel/issues/11 ,欢迎一起讨论。

RussellLuo commented 4 years ago

按照 #11 的设计,目前 timingwheel 已经增加了 ScheduleFunc ,可以支持各种 重复性任务(前提是需要实现 Scheduler)。

因为 重复性任务 形式多样,EveryFunc 代表的 “每隔固定 interval 执行的任务” 只是其中的一种特殊形式,所以 timingwheel 暂时不打算支持这种 特定 API。(不过实现这个功能也很简单,参考 EveryScheduler 的写法。)

@Allenxuxu 因为没有收到你更进一步的反馈,我打算先关闭这个 issue(以及对应的 PR )。如果有更好的建议,也欢迎再提出来一起探讨。再次感谢你的关注和参与!

Allenxuxu commented 4 years ago

@RussellLuo 国庆节快乐!

感谢大佬增加 ScheduleFunc ! 非常抱歉,没能及时回复消息(白天看过消息准备抽空回复。。。。。 后来一忙又忘记了,实在非常抱歉)。

RussellLuo commented 4 years ago

@Allenxuxu 国庆节快乐 🇨🇳 😄