Golang 并发编程(四): 定时器 与 断续器
- 陈大剩
- 2025-01-26 00:19:19
- 160
Time 包与 channel
断续器
语言中标准库包 time
中的一些 API
是用通道辅助实现的,这些 API
可以帮助我们更好的了解通道的发送和接收操作,更加有效的控制发送和接收操作。所以本节是 Golang
中 Golang 中 Channel 节拓展篇。
定时器
定时器是 time
包中结构体 Timer
类型,time
包中有两个函数能够帮助我们构建 time.Timer
类型的值,它们是 time.NewTimer
函数和 time.AfterFunc
函数。这两个函数非常简单,只需要调用时传递一个 time.Duration
类型的值就可以了。这个唯一参数就是从定时器被初始化的那一刻起,距离到期时间需要多少纳秒(ns
)。在 time.Timer
类型中,对外通知定时器到期的途径就是通道,由字段 C
代表,C
本是双向通道,C
在赋值时自动转为了接收通道。
func main() {
t := time.NewTimer(time.Second * 3)
fmt.Printf("当前时间:%v \n", time.Now())
exp := <-t.C
fmt.Printf("失效时间:%v \n", exp)
fmt.Printf("结束时间:%v \n", t.Stop())
}
使用定时器可以便捷的实现接收操作的超时设定,例如:
intChan := make(chan int, 1)
go func() {
time.Sleep(time.Second)
intChan <- 1
}()
select {
case i := <-intChan:
fmt.Printf("接收到元素的值:%v\n", i)
case <-time.NewTimer(time.Millisecond * 500).C:
fmt.Println("已经超时")
}
根据上述例子,我们很容易想到定时器用来当自旋锁最适合不过了。因为定时器是引用类型,所以定时器也可以重复使用:
intChan := make(chan int, 1)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
intChan <- i
}
close(intChan)
}()
timeout := time.Millisecond * 500
var timer *time.Timer
for {
if timer == nil {
timer = time.NewTimer(timeout)
} else {
timer.Reset(timeout)
}
select {
case i, ok := <-intChan:
if !ok {
fmt.Println("结束")
return
}
fmt.Printf("接收到元素的值:%v\n", i)
case <-timer.C:
fmt.Println("已经超时")
}
}
这里只介绍了 time.NewTimer
函数,具体 time.AfterFunc
函数可自行了解。
提示:如果厌倦了
timer.C
可以直接使用 ``time.After` 即可。
断续器
time
包中另一个重要的结构体类型是 time.Ticker
。它表示了断续器的静态结构,包含的字段和 time.Timer
一致,但行为却大不相同。定时器在重置之前只会到期一次,而断续器则会在立马到期后进入下一个周期并等待再次到期,周而复始,直到停止。
intChan := make(chan int, 1)
ticker := time.NewTicker(time.Second)
go func() {
for _ = range ticker.C {
select {
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
}
close(intChan)
fmt.Println("结束发送")
}()
var sum int
for v := range intChan {
fmt.Printf("读取到值:%v \n", v)
sum += v
if sum > 10 {
fmt.Println("大于 10 结束")
break
}
}
fmt.Println("结束接收")
上述程序中,发送方会用断续器 ticker
每隔 1 s 向 intChan
通道发送一个范围为 [1,3]
的随机数字,这个发送不会主动停止。接收方一直累加到 10
时停止(主 goroutine
结束方式)。
总结
定时器 和 断续器 都是充分利用了缓冲通道的异步特性来传达到期通知,定时器 可以用来做超时时间设计,而 断续器 可以做一些定时任务相关的设计。