go的time.Timer.stop()以及time.Ticker.stop()在关闭时不会关闭它自己的channel
因此在循环调用这两个结构的channel时要注意,避免内存泄漏。
-
封装Ticker
package ticker
import (
"fmt"
"time"
)
type Ticker struct {
Interval time.Duration
Do func()
ticker *time.Ticker
stopChannel chan bool
}
func NewTicker(d time.Duration, f func()) *Ticker {
return &Ticker{Interval: d, Do: f, ticker: time.NewTicker(d), stopChannel: make(chan bool)}
}
func (t *Ticker) Start() {
go func() {
ForBegin:
for {
select {
case <-t.ticker.C:
t.Do()
case <-t.stopChannel:
break ForBegin
}
}
fmt.Printf("exit ticker range \n")
}()
}
func (t *Ticker) Stop() {
t.stopChannel <- true
t.ticker.Stop()
fmt.Printf("stop ticker \n")
}
细节:
1.stopChannel在ticker关闭前接受结束状态,保证gorutine可以正确退出。
2.select语句中的break不会跳出循环,需要添加label再通过break跳出。
-
主程序调用代码
package main
import (
"fmt"
"gosports/lib/breakoff"
localTicker "gosports/lib/ticker"
"time"
)
func main() {
ticker := localTicker.NewTicker(time.Second, doWrite)
ticker.Start()
time.Sleep(time.Second * 5)
ticker.Stop()
fmt.Printf("out stop")
//ticker := time.NewTicker(time.Second * 5)
//
//go func(){
// for _ = range ticker.C{
// fmt.Printf("ticked at %v \n", time.Now())
// }
//}()
breakoff.Breaking()
}
func doWrite() {
fmt.Printf("ticker: %v \n", time.Now())
}
-
结果
ticker: 2019-06-28 16:22:57.30141 +0800 CST m=+1.004308335
ticker: 2019-06-28 16:22:58.301894 +0800 CST m=+2.004783118
ticker: 2019-06-28 16:22:59.297657 +0800 CST m=+3.000538026
ticker: 2019-06-28 16:23:00.298235 +0800 CST m=+4.001106591
ticker: 2019-06-28 16:23:01.298714 +0800 CST m=+5.001576388
out range
stop ticker
out stop
-
番外篇
package breakoff
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func Breaking() {
// waitting for exit signal
exit := make(chan os.Signal)
stopSigs := []os.Signal{
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGQUIT,
syscall.SIGABRT,
syscall.SIGKILL,
syscall.SIGTERM,
}
signal.Notify(exit, stopSigs...)
// catch exit signal
sign := <-exit
fmt.Printf("stop by exit signal '%s'", sign)
}
防止程序直接退出