Skip to content

Commit 1f2897b

Browse files
committed
update timer
1 parent 3a9cf8d commit 1f2897b

File tree

1 file changed

+135
-5
lines changed

1 file changed

+135
-5
lines changed

timer.md

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,71 @@
44

55
什么 Date,Parse,ParseInLocation,就不说了。主要关注下面几个函数:
66

7-
ticker 相关:
7+
### ticker 相关
88

99
```go
1010
func Tick(d Duration) <-chan Time
1111
func NewTicker(d Duration) *Ticker
1212
func (t *Ticker) Stop()
1313
```
1414

15-
timer 相关:
15+
```go
16+
package main
17+
18+
import (
19+
"fmt"
20+
"time"
21+
)
22+
23+
func main() {
24+
for t := range time.Tick(time.Second * 2) {
25+
fmt.Println(t, "hello world")
26+
}
27+
}
28+
```
29+
30+
```go
31+
package main
32+
33+
import (
34+
"fmt"
35+
"time"
36+
)
37+
38+
func main() {
39+
ticker := time.NewTicker(time.Second * 2)
40+
for {
41+
select {
42+
case t := <-ticker.C:
43+
fmt.Println(t, "hello world")
44+
}
45+
}
46+
}
47+
```
48+
49+
需要注意的是,ticker 在不使用时,应该手动 stop,如果不 stop 可能会造成 timer 泄露,比如下面这样的代码:
50+
51+
```go
52+
package main
53+
54+
import (
55+
"fmt"
56+
"time"
57+
)
58+
59+
func main() {
60+
for {
61+
select {
62+
case t := <-time.Tick(time.Second * 2):
63+
fmt.Println(t, "hello world")
64+
}
65+
}
66+
}
67+
```
68+
69+
泄露之后会在时间堆中累积越来越多的 timer 对象,从而发生内存泄露。
70+
71+
### timer 相关
1672

1773
```go
1874
func After(d Duration) <-chan Time
@@ -21,6 +77,79 @@ func (t *Timer) Reset(d Duration) bool
2177
func (t *Timer) Stop() bool
2278
```
2379

80+
time.After 一般用来控制某些耗时较长的行为,在超时后不再等待,以使程序行为可预期。如果不做超时取消释放资源,则可能因为依赖方响应缓慢而导致本地资源堆积,例如 fd,连接数,内存占用等等。从而导致服务宕机。
81+
82+
这里用阻塞 channel 读取会永远阻塞的特性来模拟较长时间的行为,在超时后会从 select 中跳出。
83+
84+
```go
85+
package main
86+
87+
import "time"
88+
89+
func main() {
90+
var ch chan int
91+
select {
92+
case <-time.After(time.Second):
93+
println("time out, and end")
94+
case <-ch:
95+
}
96+
}
97+
```
98+
99+
time.After 和 time.Tick 不同,是一次性触发的,触发后 timer 本身会从时间堆中删除。所以一般情况下直接用 `<-time.After` 是没有问题的,不过在 for 循环的时候要注意:
100+
101+
```go
102+
package main
103+
104+
import "time"
105+
106+
func main() {
107+
var ch = make(chan int)
108+
go func() {
109+
for {
110+
ch <- 1
111+
}
112+
}()
113+
114+
for {
115+
select {
116+
case <-time.After(time.Second):
117+
println("time out, and end")
118+
case <-ch:
119+
}
120+
}
121+
}
122+
```
123+
124+
上面的代码,<-ch 这个 case 每次执行的时间都很短,但每次进入 select,`time.After` 都会分配一个新的 timer。因此会在短时间内创建大量的无用 timer,虽然没用的 timer 在触发后会消失,但这种写法会造成无意义的 cpu 资源浪费。正确的写法应该对 timer 进行重用,如下:
125+
126+
```go
127+
package main
128+
129+
import "time"
130+
131+
func main() {
132+
var ch = make(chan int)
133+
go func() {
134+
for {
135+
ch <- 1
136+
}
137+
}()
138+
139+
timer := time.NewTimer(time.Second)
140+
for {
141+
timer.Reset(time.Second)
142+
select {
143+
case <-timer.C:
144+
println("time out, and end")
145+
case <-ch:
146+
}
147+
}
148+
}
149+
```
150+
151+
和 Ticker 一样,如果之前的 timer 没用了,可以手动 Stop 以使该 timer 从时间堆中移除。
152+
24153
## 源码分析
25154

26155
### 数据结构
@@ -526,7 +655,9 @@ func siftdownTimer(t []*timer, i int) {
526655
527656
```
528657

529-
### time.After
658+
### 流程
659+
660+
#### timer.After 流程
530661

531662
```go
532663
func After(d Duration) <-chan Time {
@@ -560,7 +691,6 @@ func startTimer(*runtimeTimer)
560691
func startTimer(t *timer) {
561692
addtimer(t)
562693
}
563-
564694
```
565695

566-
TODO timejump
696+
#### timer.Ticker 流程

0 commit comments

Comments
 (0)