Golang定时器断续器
定时器
1.定时器结构
结构定义
type Timer struct {
C <-chan Time // 接受定时器事件的通道
r runtimeTimer
} type runtimeTimer struct {
tb uintptr
i int when int64
period int64
f func(interface{}, uintptr) // NOTE: must not be closure
arg interface{}
seq uintptr
}
2.创建定时器
接口定义
func NewTimer(d Duration) *Timer
使用简单实例
var timer = NewTimer(time.Second) go func() {
for {
select {
case <-timer.C:
fmt.Println("time out.")
}
}
}()
NewTimer源代码:
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1) // 创建一个带有一个Time结构缓冲的通道
t := &Timer{
C: c,
r: runtimeTimer{ // 运行时定时器
when: when(d), // 定时多久
f: sendTime, // Golang写入时间的回调接口
arg: c, // 往哪个通道写入时间
},
}
startTimer(&t.r) // 启动提交定时器
return t
} // 时间到后,Golang自动调用sendTime接口,尝试往c通道写入时间
func sendTime(c interface{}, seq uintptr) {
// 给c通道以非阻塞方式发送时间
// 如果被用于NewTimer, 无论如何不能阻塞.
// 如果被用于NewTicker,接收方未及时接受时间,则会丢弃掉,因为发送时间是周期性的。
select {
case c.(chan Time) <- Now():
default:
}
} func startTimer(*runtimeTimer)
代码实例
package main import (
"fmt"
"time"
) func main() { // 创建延迟3s的定时器
exit := make(chan bool)
timer := time.NewTimer(3 * time.Second) go func() {
defer func() {
exit <- true
}() select {
case <-timer.C:
fmt.Println("time out.")
return
}
}() <-exit
}
3.停止定时器
接口定义
func (t *Timer) Stop() bool
本接口可以防止计时器出触发。如果定时器停止,则返回true,如果定时器已过期或已停止,则返回false。
Stop不关闭通道,以防止通道读取的操作不正确。为防止通过NewTimer创建的定时器,在调用Stop接口后触发,检查Stop返回值并清空通道。如:
if !t.Stop() {
<-t.C
}
但不能与Timer的通道中的其他接受同时进行!!!
对于使用AfterFunc(d, f)创建的定时器,如果t.Stop()返回false,则定时器已经过期,并且函数f已经在自己的协程中启动。
无论函数f是否执行完成,Stop()返回不会阻塞,如果用户需要知道f是否执行完毕,必须明确地与f协调。
内部使用接口
func stopTimer(*runtimeTimer) bool
代码实例
package main import (
"fmt"
"time"
) func main() {
timer := time.NewTimer(time.Second)
time.Sleep(time.Millisecond * 500)
timer.Stop() fmt.Println("timer stopped")
time.Sleep(time.Second * 3)
}
4.重置定时器
接口定义
func (t *Timer) Reset(d Duration) bool
定时器被激活,返回true,若定时器已过期或已被停止,返回false。
若程序已从t.C中接受数据,定时器已知过期,t.Rest()可直接使用
若程序还尚未从t.C收到一个值,则必须停止定时器。如果Stop提示计时器在停止之前已过期,则应明确清空通道。
if !t.Stop() {
<-t.c
}
t.Rest(d)
代码实例
package main import (
"fmt"
"time"
) func doTimer(t *time.Timer, exit chan<- bool) { go func(t *time.Timer) {
defer func() {
exit <- true
}() for {
select {
case c := <-t.C:
fmt.Println("timer timeout at", c)
return
}defer func() {
ticker.Stop()
fmt.Println("ticker stopped")
} ()
}
}(t)
} func main() {
sign := make(chan bool)
timer := time.NewTimer(time.Second * 3) doTimer(timer, sign)
time.Sleep(time.Second) // 实际测试:注释下面三行代码,效果一样。
if !timer.Stop() {
<-timer.C
} timer.Reset(time.Second * 3)
fmt.Println("timer reset at", time.Now()) <-sign
}
5.After接口
接口定义
func After(d Duration) <-chan Time
time.After函数,表示多少时间,写入当前时间,在取出channel时间之前不会阻塞,后续程序可以继续执行
time.After函数,通常用于处理程序超时问题
等待一段时间d后,Golang会发送当前时间到返回的通道上。
底层的定时器不会被GC回收,如果考虑效率,可使用NewTimer创建定时器,如果不需要,则调用Timer.Stop
源码实例
package main import (
"fmt"
"time"
) func main() {
sign := make(chan bool)
chan1 := make(chan int)
chan2 := make(chan int) defer func() {
close(sign)
close(chan1)
close(chan2)
}() go func() {
for {
select {
case c := <-time.After(time.Second * 3):
fmt.Println("After at", c)
// 若不往sign通道写入数据,程序循环每隔3s执行当前case分支。
sign <- true
case c1 := <-chan1:
fmt.Println("c1", c1)
case c2 := <-chan2:
fmt.Println("c1", c2)
}
}
}() <-sign
}
6.AfterFun接口
接口定义
func AfterFunc(d Duration, f func()) *Timer
- 等待一段时间d后,Golang会在自己的协程中调用f。并返回一个定时器,可以使用Stop方法取消调用
代码实例
package main import (
"fmt"
"time"
) func main() { timer := time.AfterFunc(time.Second*3, func() {
fmt.Println("AfterFunc Callback")
}) time.Sleep(time.Second * 5)
timer.Stop()
}
断续器
断续器(滴答器)持有一个通道,该通道每隔一段时间发送时钟的滴答
注1:从已经关闭的断续器中读取数据发生报错。所以在退出处理断续器流程前,需要先取消断续器。
注2:经过取消的断续器,不能再复用,需要重新创建一个新的断续器。
结构定义如下:
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
} type runtimeTimer struct {
tb uintptr
i int when int64
period int64
f func(interface{}, uintptr) // NOTE: must not be closure
arg interface{}
seq uintptr
}
初始化断续器
var ticker = time.NewTicker(time.Second)
取消断续器
var ticker = time.NewTicker(time.Second)
ticker.Stop()
实例一:使用Ticker(并使用时间控制ticker)
代码如下:
package main
import (
"fmt"
"time"
) func TickerTest() *time.Ticker {
// 创建一个断续器
var ticker = time.NewTicker(time.Second) go func() {
// 使用for + range组合处理断续器
for t := range ticker.C {
fmt.Println("tick at", t)
}
}() return ticker
} func main() {
ticker := TickerTest()
time.Sleep(time.Second * 10)
ticker.Stop()
}
实例二:使用channel控制ticker
参考链接:https://blog.csdn.net/yjp19871013/article/details/82048944
代码如下:
package main import (
"fmt"
"time"
) func DoTicker(ticker *time.Ticker) chan<- bool {
stopChan := make(chan bool) go func(ticker *time.Ticker) {
// 注册停止ticker方法
defer ticker.Stop()
for {
select {
// 处理断续器事件
case t := <-ticker.C:
fmt.Println("tick at", t)
// 接受外部停止断续器事件
case stop := <-stopChan:
if stop {
fmt.Println("DoTicker Exit")
return
}
}
}
}(ticker) // 返回由外部控制Ticker停止的Channel
return stopChan
} func main() { var ticker = time.NewTicker(time.Second)
stopChan := DoTicker(ticker) time.Sleep(time.Second * 10)
// 停止断续器
stopChan <- true
time.Sleep(time.Second)
close(stopChan)
}
实例三:使用channel控制停止ticker
代码如下:
package main import (
"fmt"
"time"
) func DoTicker(ticker *time.Ticker, times int) {
// 创建有times个缓冲的byte通道
stopChan := make(chan byte, times) go func(ticker *time.Ticker) { defer func() {
// 经过调试,defer语句块并未执行
ticker.Stop()
fmt.Println("ticker stopped")
} () for t := range ticker.C {
fmt.Println("write stop channel") // 写满times次后,当前goroutine自动退出
stopChan <- 0
fmt.Println("tick at", t)
} // 经调试,该语句并未执行
fmt.Println("DoTicker1 Exit")
}(ticker)
} func main() {
var ticker = time.NewTicker(time.Second) DoTicker(ticker, 5)
time.Sleep(time.Second * 10)
}
调试输出:
write stop channel
tick at 2019-03-13 11:44:35.932692894 +0800 CST m=+1.000442776
write stop channel
tick at 2019-03-13 11:44:36.932643384 +0800 CST m=+2.000393270
write stop channel
tick at 2019-03-13 11:44:37.932565147 +0800 CST m=+3.000315031
write stop channel
tick at 2019-03-13 11:44:38.932735589 +0800 CST m=+4.000485469
write stop channel
tick at 2019-03-13 11:44:39.932553565 +0800 CST m=+5.000303443
write stop channel Process finished with exit code 0
Golang定时器断续器的更多相关文章
- [Go] golang定时器与redis结合
golang定时器与redis结合,每隔1秒ping一下,每隔20秒llen一下队列的长度 package main import ( "fmt" "time" ...
- [Go] golang定时器的使用
golang中的定时器是使用的chanel阻塞来实现的,主要使用到了time包中的内容,如果有多个定时器的channel,为了防止阻塞,可以使用select来获取遍历channel 定时器获取的cha ...
- JS 定时器/延时器
定时器 创建定时器 window.setInterval(方法类型,间隔时间(1000=1秒)) var timer=window.setInterval(func,2000); var i=0 ...
- golang 定时器
上网查了下相关资料,基本上都介绍的是github.com\robfig\cron这个包来执行定时任务,试了下确实可以执行.但是此包下没有删 除任务的方法,只有暂停的方法(Stop),若要停止之前的任务 ...
- 一些css3的特效 javascript的window对象 定时器 延时器等ing...
风车转动代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...
- JS延时器 定时器 暂停器 中断器
// numberMillis 毫秒 function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() ...
- [Golang]-7 定时器和打点器
目录 定时器 打点器 After()方法 我们常常需要在未来某个时刻运行 Go 代码,或者在某段时间间隔内重复运行. Go 的内置 定时器 和 打点器 特性让这些很容易实现. 定时器 type Tim ...
- 谈谈Golang中goroutine的调度问题
goroutine的调度问题,同样也是我之前面试的问题,不过这个问题我当时并不是很清楚,回来以后立马查阅资料,现整理出来备忘. 有一些预备知识需要说明,就是操作系统中的线程.操作系统中的线程分为两种: ...
- Go_20: Golang 中 time 包的使用
time包中包括两类时间:时间点(某一时刻)和时常(某一段时间) 1. 时间常量(时间格式化) const ( ANSIC = "Mon Jan _2 15:04:05 2006" ...
随机推荐
- link 和 import 导入外部样式的区别
差别一:link 属于 XHTML 标签,而 @import 完全是 CSS 提供的 一种方式.link标签除了可以加载 CSS 外,还可以做很多事情,比如定义 RSS ,定义 rel 链接属性等. ...
- CSS基础语法与选择器
CSS基础 语法 : <head> <style type="text/css"> 选择器(即修饰对象){ 修饰属性:属性值; 修饰属性:属性值; } &l ...
- 采用C/C++语言如何实现复数抽象数据类型Complex
记录一下! 采用C/C++语言如何实现复数抽象数据类型Complex #include <stdio.h> typedef struct Complex { double e1; // 实 ...
- String字符串操作
char chars[] ={'a','b','c'}; String s = new String(chars); int len = s.length();//字符串长度 System.out.p ...
- 详细分析UserLock如何保证高校内部信息安全
俄克拉荷马城市公立学校的IT团队负责该片区接近43000个学生的网络管理工作.长期以来,学生和教师员工共享Windows网络登录为他们带来了很多难题. 由于没有并发登录的限制,也不能对网络使用情况进行 ...
- dwr2.0版本的demo
谈起DWR,这个东西在上学的时候接触过,但工作之后就再也没有用过. 对DWR的印象是不手写AJAX,使用JavaScript调用java后台的代码,就如同调用前台代码一样. ...
- visual studio code配置项
// 通过将设置放入设置文件中来覆盖设置. { //-------- 编辑器配置 -------- // 控制字体系列. "editor.fontFamily": "Co ...
- (EXPDP) Fails With Errors ORA-39079 ORA-25306 On One Node In RAC Environment
分类: Oracle DataPump export on one certain RAC instance fails with errors: ORA-39006: internal errorO ...
- WP模拟器修改语言为中文方法
对于WP7模拟器来说默认启动时显示的语言为英文,除了操作界面外,如果你的应用支持多国语言必须修改为中文才能显示正确的界面.下面Zune123将WP7 Emulator修改语言为中文的方法写个简单的教程 ...
- 用批处理在windows中导出/导入无线网络信息,复制保存为bat即可
@echo offtitle 在windows中导出/导入无线网络信息 :Beginecho ========================echo 请选择操作:echo 1 查看可用的无线网络ec ...