golang 循环执行定时任务

简介: golang 循环执行定时任务

有时我们想在自己的服务里单独弄一个定时器,但是又不想让定时器的定时任务成为主线程,而是作为 http 服务或者 rpc 服务的一个子线程来执行任务。

一、定时器 NewTicker

1、第一种写法

package main
import (
    "time"
    "fmt"
)
func printDemo() {
  fmt.Println("demo........")
}
// 初始化 demo 定时器
func InitDemoScheduler()  {
    // 每 5 秒钟时执行一次
    ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
    go func() { // 用新协程去执行定时任务
        defer func() {
    if r := recover(); r != nil {
      logs.Error("定时器发生错误,%v", r)
    }
    ticker.Stop() // 意外退出时关闭定时器
  }()
        printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
        for  { // 用上一个死循环,不停地执行,否则只会执行一次
            select { 
                case <- ticker.C: // 时间到了就会触发这个分支的执行,其实时间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到channel中有数据可读,就读取channel数据,执行相应分支的语句
                    printDemo()   
            }
        }
    }()
}
func main(){
  // 初始化定时器,每 5s 会打印一个「demo........」
  InitDemoScheduler()
  // 等待,避免主线程退出,实际应用时这里可以时启动 http 服务器的监听动作,或者启动 rpc 服务的监听动作,所以不需要 sleep
  time.sleep(100*time.Second)
}

case <- ticker.C: 间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到 channel 中有数据可读,就读取 channel 数据,并执行相应分支的语句,select 语法可以参考:golang中的select详解(转)

还有个需要注意的点是:如果需要在协程启动时执行一次任务,需要在在 for 循环之前额外执行一次,这样在协程启动时会启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务


2、另一种写法


发现一种新的写法,这种写法没有用 select 语法,而是通过 for 循环从 channel 中取数,理论上应该也可以。但是没实验过,不知道行不行,

// 初始化 demo 定时器
func InitDemoScheduler()  {
  // 每 5 秒钟时执行一次
  ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
  go func() { // 用新协程去执行定时任务
    defer func() {
      if r := recover(); r != nil {
        logs.Error("定时器发生错误,%v", r)
      }
      ticker.Stop() // 意外退出时关闭定时器
    }()
                printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
    for _ = range ticker.C {
      printDemo()
    }
  }()
}


3、可随时退出的定时任务


上面的两种写法,定时器在创建完成后,协程永远无法退出,如果你想提前退出,可以使用一个无缓冲泳道,对外提供一个往该泳道发送消息的函数,定时任务进程在监听定时器的同时也监听这个无缓冲泳道,如果监听到无缓冲泳道的消息,则立刻 return 终止协程,也就终止了定时任务。

package main
import (
    "time"
    "fmt"
)
func printDemo() {
  fmt.Println("demo........")
}
// 一个无缓冲泳道
var stopFlag = make(chan bool)
// 对外提供一个往泳道写消息的函数,如果想关闭定时任务,调用该函数即可。
func CloseDemoScheduler()  {
  stopFlag <- false
}
// 初始化 demo 定时器
func InitDemoScheduler()  {
  // 每 5 秒钟时执行一次
  ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
  go func() { // 用新协程去执行定时任务
    defer func() {
      if r := recover(); r != nil {
        logs.Error("定时器发生错误,%v", r)
      }
      ticker.Stop() // 意外退出时关闭定时器
    }()
    printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
    for  { // 用上一个死循环,不停地执行,否则只会执行一次
      select {
      case <- ticker.C: // 时间到了就会触发这个分支的执行,其实时间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到channel中有数据可读,就读取channel数据,执行相应分支的语句
        printDemo()
      case <- stopFlag: // 定时任务进程在监听定时器的同时也监听这个无缓冲泳道,如果监听到无缓冲泳道的消息,则立刻 return 终止协程,也就终止了定时任务。
        return
      }
    }
  }()
}
func main(){
  // 初始化定时器,每 5s 会打印一个「demo........」
  InitDemoScheduler()
  // 等待,避免主线程退出,实际应用时这里可以时启动 http 服务器的监听动作,或者启动 rpc 服务的监听动作,所以不需要 sleep
  time.sleep(100*time.Second)
}

二、另一种 定时器 Ticker

这种定时器 除了创建定时器调用的是 time.Tick 而不是 time.NewTicker 外,用法跟 NewTicker 完全相同,区别是 Ticker 无法被关闭停止,所以也不需要在 defer 中关闭 Ticker

tick := time.Tick(time.Second)

相关文章
|
2天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
20 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
10月前
|
Go
golang中for循环的遍历特点、数据类型和string转换
golang中for循环的遍历特点、数据类型和string转换
|
10月前
|
JavaScript 前端开发 Go
正确使用 Golang 循环创建新切片的方式
正确使用 Golang 循环创建新切片的方式
|
11月前
|
Go
Golang 基础案例集合:中文拼音转换、解析二维码、压缩 zip、执行定时任务
曾经,因为不够注重基础吃了好多亏。总是很喜欢去看那些高大上的东西,却忽略了最基本的东西。然后会错误的以为自己懂的很多,但是其实是沙堆中筑高台,知道很多高大上的架构,但是基础的东西却不太了解。我觉得,可
227 0
Golang:robfig/cron执行定时任务
Golang:robfig/cron执行定时任务
452 0
|
Go 调度 数据库
Golang:gocron定时任务管理系统的安装与使用
Golang:gocron定时任务管理系统的安装与使用
392 0
Golang:gocron定时任务管理系统的安装与使用
golang 循环创建新协程,发现每次使用的循环变量都一样,都是最后一个
golang 循环创建新协程,发现每次使用的循环变量都一样,都是最后一个
golang 循环创建新协程,发现每次使用的循环变量都一样,都是最后一个
|
Go
Golang学习之路(四):Golang的循环语句和循环控制语句
Golang学习之路(四):Golang的循环语句和循环控制语句
97 0
|
设计模式 Java Go
Golang代码循环依赖问题
Golang代码循环依赖问题
464 0
Golang代码循环依赖问题
|
存储 编译器 Go
Golang语言循环、指针、结构体和切片(打卡✏️第二天)|Go主题月(下)
Go 语言循环语句 for循环是一个循环控制结构,可以执行指定次数的循环。
260 0