Hike News
Hike News

Golang多線程初識-day28-定時器(超時處理)

introduction

  • 設置超時時間(timeout),避免阻塞時間過長
  • 定時器位於time包中,使用NewTicker()方法生成定時器

生成定時器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"time"
"fmt"
)

func main(){
//使用NewTicker設置定時器,參數為時間的間隔
//以下為設置一個一秒執行一次的定時器
timer := time.NewTicker(time.Second)

//使用range()方法從timer.C管道中取出數據
for v:= range(timer.C) {
fmt.Println("hello",v)
}
}
  • timer.C 本身為一個channel,其後端有一個goroutine,每隔一段NewTicker()方法設置的時間參數,會往裡面寫入時間
  • 上述利用range()方法將數據取出

result

1
2
3
4
5
hello 2018-05-19 18:18:47.193000586 +0800 CST m=+1.004599317
hello 2018-05-19 18:18:48.190658733 +0800 CST m=+2.002270359
hello 2018-05-19 18:18:49.189746058 +0800 CST m=+3.001370599
...
...
  • 因為每隔一秒goroutine才會往timer.C管道扔數據,因此主線程也是每一秒才能從管道中取出數據

一次性定時器

  • 搭配select一起使用,仍是用作設置timeout
  • time.After(Time)的參數為一時間

example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"time"
"fmt"
)

func main(){
IntChan1 := make(chan int,1)
IntChan2 := make(chan int,2)
i := 0
for {
//time.After開始計時
select {
case IntChan1 <- i:
fmt.Printf("i value %d in Chan 1\n",i)
case IntChan2 <- i:
fmt.Printf("i value %d in Chan 2\n",i)

//滿足time.After()參數設置的時間,
//表示無法滿足上面任一case操作(兩管道皆已滿),操作已超時
//將time.After()管道中的時間丟棄,並完成select操作
case <- time.After(time.Second):
fmt.Println("cross 1 second: timeout")
}
i++
}
}

result

1
2
3
4
5
6
7
8
i value 0 in Chan 1
i value 1 in Chan 2
i value 2 in Chan 2
cross 1 second: timeout
cross 1 second: timeout
cross 1 second: timeout
...
...

tips

  • 較不推薦使用此方法作為超時控制,因為time.After()並未關閉,
  • 在for無限循環中可以看到,會創建越來越多的管道造成內存overflow

example 2

  • 使用time.NewTicker()作為一次性的計時器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"time"
"fmt"
)

func main(){
IntChan1 := make(chan int,1)
IntChan2 := make(chan int,2)

timer := time.NewTicker(time.Second)

i := 0
for {
select {
case IntChan1 <- i:
fmt.Printf("i value %d in Chan 1\n",i)
time.Sleep(time.Millisecond*800)
case IntChan2 <- i:
fmt.Printf("i value %d in Chan 2\n",i)
time.Sleep(time.Millisecond*800)

//與上個例子一樣從管道中取出時間丟棄
case <- timer.C:
fmt.Println("cross 1 second: timeout")
}
i++

//使用完一次性的計時器隨即關閉,關閉後對象就消失,在使用此對象會panic
timer.Stop()
}
}

result

1
2
3
4
i value 0 in Chan 1
i value 1 in Chan 2
i value 2 in Chan 2
fatal error: all goroutines are asleep - deadlock!
  • 第一次select已經關閉timer,但管道未滿,
  • 第四次select後,timer已關閉,再次使用發生panic

tips

推薦使用第二種方式當作一次性的定時器