Skip to content

Commit 16de59f

Browse files
committed
bugfix 修复周期性定时器不准的问题
1 parent ffc9f77 commit 16de59f

File tree

2 files changed

+117
-7
lines changed

2 files changed

+117
-7
lines changed

time_wheel.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ func (t *timeWheel) add(node *timeNode, jiffies uint64) *timeNode {
114114
break
115115
}
116116
}
117-
//fmt.Printf("idx:%d i:%p\n", idx, head)
118117
}
119118

120119
if head == nil {
@@ -202,18 +201,18 @@ func (t *timeWheel) moveAndExec() {
202201
// return
203202
}
204203

205-
//如果本层的盘子没有定时器,这时候和上层的盘子移动一些过来
204+
//如果本层的盘子没有定时器,这时候从上层的盘子移动一些过来
206205
index := t.jiffies & nearMask
207206
if index == 0 {
208207
for i := 0; i <= 3; i++ {
209208
index2 := t.index(i)
209+
t.cascade(i, int(index2))
210210
if index2 != 0 {
211-
t.cascade(i, int(index2))
212211
break
213212
}
214213
}
215214
}
216-
215+
217216
atomic.AddUint64(&t.jiffies, 1)
218217

219218
t.t1[index].Lock()
@@ -228,8 +227,7 @@ func (t *timeWheel) moveAndExec() {
228227
atomic.AddUint64(&t1.version, 1)
229228
t.t1[index].Unlock()
230229

231-
// 执行
232-
230+
// 执行,链表中的定时器
233231
offset := unsafe.Offsetof(head.Head)
234232

235233
head.ForEachSafe(func(pos *list.Head) {
@@ -244,7 +242,10 @@ func (t *timeWheel) moveAndExec() {
244242

245243
if val.isSchedule {
246244
jiffies := t.jiffies
247-
val.expire = uint64(getExpire(val.userExpire, jiffies))
245+
// 这里的jiffies必须要减去1
246+
// 当前的callback被调用,已经包含一个时间片,如果不把这个时间片减去,
247+
// 每次多一个时间片,就变成累加器, 最后周期定时器慢慢会变得不准
248+
val.expire = uint64(getExpire(val.userExpire, jiffies-1))
248249
t.add(val, jiffies)
249250
}
250251
})

time_wheel_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package timer
22

33
import (
4+
"context"
45
"math"
6+
"sync/atomic"
57
"testing"
68
"time"
79

@@ -24,6 +26,7 @@ func Test_GenVersion(t *testing.T) {
2426
assert.Equal(t, genVersionHeight(1, 64), uint64(0x0001004000000000))
2527
}
2628

29+
// 测试1小时
2730
func Test_hour(t *testing.T) {
2831
tw := newTimeWheel()
2932

@@ -48,3 +51,109 @@ func Test_hour(t *testing.T) {
4851
}
4952
assert.True(t, *testHour)
5053
}
54+
55+
// 测试周期性定时器, 5s
56+
func Test_ScheduleFunc_5s(t *testing.T) {
57+
tw := newTimeWheel()
58+
59+
var first5 int32
60+
ctx, cancel := context.WithCancel(context.Background())
61+
62+
const total = int32(1000)
63+
64+
testTime := time.Second * 5
65+
66+
tw.ScheduleFunc(testTime, func() {
67+
atomic.AddInt32(&first5, 1)
68+
if atomic.LoadInt32(&first5) == total {
69+
cancel()
70+
}
71+
72+
})
73+
74+
expire := getExpire(testTime*time.Duration(total), 0)
75+
for i := 0; i <= int(expire)+10; i++ {
76+
get10Ms := func() time.Duration {
77+
return tw.curTimePoint + 1
78+
}
79+
tw.run(get10Ms)
80+
}
81+
82+
select {
83+
case <-ctx.Done():
84+
case <-time.After(time.Second / 100):
85+
}
86+
87+
assert.Equal(t, total, first5)
88+
89+
}
90+
91+
// 测试周期性定时器, 1hour
92+
func Test_ScheduleFunc_hour(t *testing.T) {
93+
tw := newTimeWheel()
94+
95+
var first5 int32
96+
ctx, cancel := context.WithCancel(context.Background())
97+
98+
const total = int32(100)
99+
testTime := time.Hour
100+
101+
tw.ScheduleFunc(testTime, func() {
102+
atomic.AddInt32(&first5, 1)
103+
if atomic.LoadInt32(&first5) == total {
104+
cancel()
105+
}
106+
107+
})
108+
109+
expire := getExpire(testTime*time.Duration(total), 0)
110+
for i := 0; i <= int(expire)+10; i++ {
111+
get10Ms := func() time.Duration {
112+
return tw.curTimePoint + 1
113+
}
114+
tw.run(get10Ms)
115+
}
116+
117+
select {
118+
case <-ctx.Done():
119+
case <-time.After(time.Second / 100):
120+
}
121+
122+
assert.Equal(t, total, first5)
123+
124+
}
125+
126+
// 测试周期性定时器, 1day
127+
func Test_ScheduleFunc_day(t *testing.T) {
128+
tw := newTimeWheel()
129+
130+
var first5 int32
131+
ctx, cancel := context.WithCancel(context.Background())
132+
133+
const total = int32(10)
134+
testTime := time.Hour * 24
135+
136+
tw.ScheduleFunc(testTime, func() {
137+
atomic.AddInt32(&first5, 1)
138+
if atomic.LoadInt32(&first5) == total {
139+
cancel()
140+
}
141+
142+
})
143+
144+
expire := getExpire(testTime*time.Duration(total), 0)
145+
for i := 0; i <= int(expire)+10; i++ {
146+
get10Ms := func() time.Duration {
147+
return tw.curTimePoint + 1
148+
}
149+
tw.run(get10Ms)
150+
}
151+
152+
select {
153+
case <-ctx.Done():
154+
case <-time.After(time.Second / 100):
155+
}
156+
157+
assert.Equal(t, total, first5)
158+
159+
}

0 commit comments

Comments
 (0)