4
4
5
5
什么 Date,Parse,ParseInLocation,就不说了。主要关注下面几个函数:
6
6
7
- ticker 相关:
7
+ ### ticker 相关
8
8
9
9
``` go
10
10
func Tick (d Duration ) <-chan Time
11
11
func NewTicker(d Duration) *Ticker
12
12
func (t *Ticker) Stop()
13
13
```
14
14
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 相关
16
72
17
73
``` go
18
74
func After (d Duration ) <-chan Time
@@ -21,6 +77,79 @@ func (t *Timer) Reset(d Duration) bool
21
77
func (t *Timer) Stop() bool
22
78
```
23
79
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
+
24
153
## 源码分析
25
154
26
155
### 数据结构
@@ -526,7 +655,9 @@ func siftdownTimer(t []*timer, i int) {
526
655
▼
527
656
```
528
657
529
- ### time.After
658
+ ### 流程
659
+
660
+ #### timer.After 流程
530
661
531
662
``` go
532
663
func After (d Duration ) <-chan Time {
@@ -560,7 +691,6 @@ func startTimer(*runtimeTimer)
560
691
func startTimer(t *timer) {
561
692
addtimer (t)
562
693
}
563
-
564
694
```
565
695
566
- TODO timejump
696
+ #### timer.Ticker 流程
0 commit comments