服务器执行函数时如何避免Ticker泄漏 - Golang实践
服务器执行函数时如何避免Ticker泄漏 - Golang实践
你好,我有一个这样的问题。一般来说,有一个服务器会处理某个函数,这个函数设置了一个定时器(ticker)。当这个函数结束时,会调用 ticker.Stop() 来停止它。问题在于,如果我不是从服务器运行这个函数,一切正常,定时器会随着函数执行完毕而停止。但是,当从服务器启动时,定时器会无限期地泄漏。
定时器函数本身:
func cmdTicker() {
ticker := time.NewTicker(time.Second)
now := time.Now()
for range ticker.C {
fmt.Println(fmt.Sprintf("%s", time.Since(now)))
//timeProc = append(timeProc, fmt.Sprintf("%s", time.Since(now)))
}
ticker.Stop()
}
定时器启动:
func Scan(cmd *pb.ScanCommandReq) []*models.Cmdprun {
wgAllScan := new(sync.WaitGroup)
//while the function is running ticker goes
go cmdTicker()
go func(wg *sync.WaitGroup) {
for res := range resultsCh {
if res.stateProc == 0 {
host, err := resultfiles.ParseXMLResultFile(res.fileScan)
if err != nil {
log.Printf("Parsing file not done: %s", err)
}
results = append(results, host)
err = os.Remove(res.fileScan)
if err != nil {
log.Fatal("Can't delete file", res.fileScan, err)
}
}
wg.Done()
}
}(wgAllScan)
wgAllScan.Wait()
close(resultsCh)
return results
}
更多关于服务器执行函数时如何避免Ticker泄漏 - Golang实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
我看到你的问题时也想到了同样的事情,但我之前没来得及早点回复你!很高兴你自己解决了问题并分享了解决方案 👍
更多关于服务器执行函数时如何避免Ticker泄漏 - Golang实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
当面临类似任务时,有人能派上用场。通过检查以从通道获取值并停止计时器来解决此问题。
func cmdTicker() {
ticker := time.NewTicker(time.Second)
now := time.Now()
for {
// Select 语句
select {
// Case 语句
case <-chTicker:
ticker.Stop()
fmt.Println("扫描完成!")
return
// 打印计时器的 Case
case <-ticker.C:
fmt.Println(fmt.Sprintf("%s", time.Since(now)))
}
}
}
在函数本身中,我们发送一个成功完成的信号。
chTicker <- true
在服务器环境中,cmdTicker() 函数中的 ticker.Stop() 永远不会被执行,因为 for range ticker.C 会无限循环。你需要通过上下文或通道来控制定时器的退出。以下是修复后的代码示例:
func cmdTicker(ctx context.Context) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop() // 确保退出时停止
now := time.Now()
for {
select {
case <-ctx.Done():
return // 上下文取消时退出
case <-ticker.C:
fmt.Printf("%s\n", time.Since(now))
}
}
}
启动定时器时需要传入上下文:
func Scan(cmd *pb.ScanCommandReq) []*models.Cmdprun {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 函数结束时取消上下文
wgAllScan := new(sync.WaitGroup)
// 传入上下文控制定时器退出
go cmdTicker(ctx)
go func(wg *sync.WaitGroup) {
for res := range resultsCh {
if res.stateProc == 0 {
host, err := resultfiles.ParseXMLResultFile(res.fileScan)
if err != nil {
log.Printf("Parsing file not done: %s", err)
}
results = append(results, host)
err = os.Remove(res.fileScan)
if err != nil {
log.Printf("Can't delete file %s: %v", res.fileScan, err)
}
}
wg.Done()
}
}(wgAllScan)
wgAllScan.Wait()
close(resultsCh)
return results
}
或者使用通道控制退出:
func cmdTicker(stopCh <-chan struct{}) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
now := time.Now()
for {
select {
case <-stopCh:
return
case <-ticker.C:
fmt.Printf("%s\n", time.Since(now))
}
}
}
// 使用方式
stopCh := make(chan struct{})
go cmdTicker(stopCh)
// 需要停止时:close(stopCh)
关键点:
- 使用
defer ticker.Stop()确保资源释放 - 通过
select语句同时监听定时器和退出信号 - 服务器函数结束时发送退出信号(上下文取消或关闭通道)

