Go语言定时任务的实现示例

 更新时间:2023年05月31日 10:31:42   作者:zsx_yiyiyi  
本文主要介绍了Go语言定时任务的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

robfig/cron 是Go语言实现的开源定时任务调度框架,核心代码是巧妙的使用chan + select + for实现了一个轻量

级调度协程,不但语法简洁,而且具有很好的性能。

Cron是Go中用于设置定时任务的一个库,需要注意的是,Cron库分两个大版本,v1.2和v3.0,其功能和go get地

址都是不同的,注意区分。

v1.2官方文档:https://pkg.go.dev/github.com/robfig/cron

v3官方文档:https://pkg.go.dev/github.com/robfig/cron/v3

cron github仓库:https://github.com/robfig/cron

1、安装依赖

# v1.2
go get github.com/robfig/cron
# v3
go get github.com/robfig/cron/v3@v3.0.0

2、定时任务简单案例

package main
import (
    "fmt"
    "github.com/robfig/cron/v3"
    "time"
)
func main() {
    // 新建一个定时任务对象,根据cron表达式进行时间调度,cron可以精确到秒,大部分表达式格式也是从秒开始
    // 默认从分开始进行时间调度
    // cronTab := cron.New()
    // 精确到秒
    cronTab := cron.New(cron.WithSeconds())
    // 定义定时器调用的任务函数
    task := func() {
        fmt.Println("hello world", time.Now())
    }
    // 定时任务,cron表达式,每五秒一次
    spec := "*/5 * * * * ?"
    // 添加定时任务
    cronTab.AddFunc(spec, task)
    // 启动定时器
    cronTab.Start()
    // 阻塞主线程停止
    select {}
}

# 输出信息
hello world 2023-05-30 12:48:40.0089132 +0800 CST m=+0.087419701
hello world 2023-05-30 12:48:45.0040694 +0800 CST m=+5.082575901
hello world 2023-05-30 12:48:50.001667 +0800 CST m=+10.080173501
hello world 2023-05-30 12:48:55.0013075 +0800 CST m=+15.079814001
hello world 2023-05-30 12:49:00.0011284 +0800 CST m=+20.079634901
hello world 2023-05-30 12:49:05.0080655 +0800 CST m=+25.086572001
......

3、Cron表达式

cron 表达式是一个好东西,这个东西不仅 Java 的 quartZ 能用到,Go 语言中也可以用到。

Linux 也是可以用 crontab -e 命令来配置定时任务。

Go 语言和 Java 中都是可以精确到秒的,但是 Linux 中不行。

cron表达式代表一个时间的集合,使用6个空格分隔的字段表示:

字段名是否必须允许的值允许的特定字符
秒(Seconds)0-59* / , -
分(Minute)0-59* / , -
时(Hours)0-23* / , -
日(Day of month)1-31* / , - ?
月(Month)1-12 或 JAN-DEC* / , -
星期(Day of week)0-6 或 SUM-SAT* / , - ?

特殊字符说明:

? 只能在 day 和 week 中使用,标识未说明的值,用以解决 day 和 week 的冲突,比如 * * * 10 * ? 表示每

月10号触发,而换成 * 则表示不管星期几都可触发,与前者发生冲突。

3.1 Cron表达式说明

  • 月(Month)和星期(Day of week)字段的值不区分大小写,如:SUN、Sun 和 sun 是一样的。
  • 星期(Day of week)字段如果没提供,相当于是 *

3.2 Cron表达式示例说明

如果我们使用 crontab := cron.New(cron.WithSeconds()),我们的定时任务表达式需要为:

* * * * * *

如果我们使用 cronTab := cron.New(),我们的定时任务表达式需要为:

* * * * *,不包含秒

这 6 个 * 分别代表什么意思呢?

# 第一个*: second,范围(0 - 60)
# 第二个*: min,范围(0 - 59)
# 第三个*: hour,范围(0 - 23)
# 第四个*: day of month,范围(1 - 31)
# 第五个*: month,范围(1 - 12)
# 第六个*: day of week,范围(0 - 6) (0 to 6 are Sunday to Saturday)
* * * * * *

3.3 cron特定字符说明

符号说明
(*)表示 cron 表达式能匹配该字段的所有值。如在第5个字段使用星号(month),表示每个月
(/)表示增长间隔,如第1个字段(minutes) 值是 3-59/15,表示每小时的第3分钟开始执行一次,之后每隔 15 分钟执行一次(即 3、18、33、48 这些时间点执行),这里也可以表示为:3/15
(,)用于枚举值,如第6个字段值是 MON,WED,FRI,表示 星期一、三、五 执行
(-)表示一个范围,如第3个字段的值为 9-17 表示 9am 到 5pm 直接每个小时(包括9和17)
(?)只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *

3.4 常用cron举例

每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每周一和周三晚上22:30: 00 30 22 * * 1,3 
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
每年三月的星期四的下午14:10和14:40:  00 10,40 14 ? 3 4 

3.5 预定义的时间格式

您可以使用几个预定义的表达式来代替上表的表达式,使用如下:

输入描述等式
@yearly (or @annually)每年一次,1月1日午夜0 0 0 1 1 *
@monthly每月运行一次,每月第一天午夜0 0 0 1 * *
@weekly每周运行一次,周六/周日之间的午夜0 0 0 * * 0
@daily (or @midnight)每天午夜运行一次0 0 0 * * *
@hourly每小时运行一次0 0 * * * *

还可以安排作业以固定的间隔执行,从添加作业或运行cron时开始。这是通过如下格式化cron规范来支持的:

@every <duration>
// 例如
c := cron.New() 
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })

4、时区

默认情况下,所有时间都是基于当前时区的,也可自定义:在时间字符串前面添加一个CRON_TZ= + 具体时区

一些常用的时区:

  • 东京时区:Asia/Tokyo
  • 纽约时区:America/New_York
  • 上海时区:Asia/Shanghai
  • 香港时区:Asia/Hong_Kong

创建cron对象时增加一个时区选项cron.WithLocation(location),location为time.LoadLocation(zone)加载的时区

对象,zone为具体的时区格式。

package main
import (
    "fmt"
    "github.com/robfig/cron/v3"
    "time"
)
func main() {
    //直接配置时区
    nyc, _ := time.LoadLocation("America/New_York")
    // cron.New(cron.WithLocation(time.UTC))
    c := cron.New(cron.WithLocation(nyc),cron.WithSeconds())
    c.AddFunc("*/5 * * * * ?", func() {
        fmt.Println("Every 5 second at New York")
    })
    // 参数里面配置时区
    c.AddFunc("CRON_TZ=Asia/Tokyo */5 * * * * ?", func() {
        fmt.Println("Every 5 second at Tokyo")
    })
    c.Start()
    select {}
}

# 输出
Every 5 second at New York
Every 5 second at Tokyo
Every 5 second at Tokyo
Every 5 second at New York
......

5、自定义定时任务以及多个定时任务

自定义定时任务只需要实现Job接口的Run方法。

package main
import (
    "fmt"
    "github.com/robfig/cron/v3"
    "time"
)
type Task1 struct {
    Name string
}
// 自定义定时任务只需要实现Job接口的Run方法
func (t *Task1) Run() {
    fmt.Println("Task1: ", t.Name)
}
type Task2 struct {
    Name string
}
// 自定义定时任务只需要实现Job接口的Run方法
func (t *Task2) Run() {
    fmt.Println("Task2: ", t.Name)
}
func main() {
    cronTab := cron.New(cron.WithSeconds())
    // 定义定时器调用的任务函数
    // 定时任务
    // cron表达式,每五秒一次
    spec := "*/5 * * * * ?"
    //定义定时器调用的任务函数
    task := func() {
        fmt.Println("hello world", time.Now())
    }
    // 添加多个定时器
    cronTab.AddFunc(spec, task)
    cronTab.AddJob(spec, &Task1{Name: "tom"})
    cronTab.AddJob(spec, &Task2{Name: "merry"})
    // 启动定时器
    cronTab.Start()
    // 关闭,但是不能关闭已经在执行中的任务
    defer cronTab.Stop()
    // 阻塞主线程停止
    select {}
}

# 输出
Task1:  tom
Task2:  merry
hello world 2023-05-30 15:03:55.006422 +0800 CST m=+1.424751701
Task2:  merry
Task1:  tom
hello world 2023-05-30 15:04:00.0057737 +0800 CST m=+6.424103401
Task2:  merry
Task1:  tom
hello world 2023-05-30 15:04:05.0003003 +0800 CST m=+11.418630001
......

6、主要类型或接口说明

6.1 Job

任务抽象(业务隔离):任务抽象成一个 Job 接口,业务逻辑类只需实现该接口。

每一个实体包含一个需要运行的 Job,这是一个接口,只有一个方法:Run。

// Job is an interface for submitted cron jobs.
type Job interface {
    Run()
}

由于 Entity 中需要 Job 类型,因此,我们希望定期运行的任务,就需要实现 Job 接口。同时,由于 Job 接口只有一个无参数无返回值的方法,为了使用方便,作者提供了一个类型

// 它通过简单的实现Run()方法来实现Job接口
type FuncJob func()
// 这样,任何无参数无返回值的函数,通过强制类型转换为FuncJob,就可以当作Job来使用了,AddFunc方法就是这么做的
func (f FuncJob) Run() { f() }

6.2 Schedule

计划接口:通过当前时间计算任务的下次执行执行时间,具体实现类可以根据实际需求实现。

每个实体包含一个调度器(Schedule)负责调度 Job 的执行。它也是一个接口,Schedule 的具体实现通过解析 Cron

表达式得到。库中提供了 Schedule 的两个具体实现,分别是 SpecSchedule 和 ConstantDelaySchedule。

// Schedule describes a job's duty cycle.
type Schedule interface {
    // Next returns the next activation time, later than the given time.
    // Next is invoked initially, and then each time the job is run.
    // 返回同一Entity中的Job下一次执行的时间
    Next(time.Time) time.Time
}

6.2.1 SpecSchedule

从开始介绍的 Cron 表达式可以容易得知各个字段的意思,同时,对各种表达式的解析也会最终得到一个

SpecSchedule 的实例。库中的 Parse 返回的其实就是 SpecSchedule 的实例(当然也就实现了 Schedule 接口)。

// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
    Second, Minute, Hour, Dom, Month, Dow uint64
    // Override location for this schedule.
    Location *time.Location
}

该类的 Next 方法实现比较多,这里就不介绍了。

6.2.2 ConstantDelaySchedule

// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
    // 循环的时间间隔
    Delay time.Duration
}

这是一个简单的循环调度器,如:每 5 分钟。注意,最小单位是秒,不能比秒还小,比如毫秒。

实现:

// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
    return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
}

通过 Every 函数可以获取该类型的实例,如下得到的是一个每 5 秒执行一次的调度器。

package main
import (
    "fmt"
    "github.com/robfig/cron/v3"
)
func main() {
    constDelaySchedule1 := cron.Every(5e9)
    // 5s
    fmt.Println(constDelaySchedule1.Delay)
    constDelaySchedule2 := cron.Every(5e6)
    // 1s
    fmt.Println(constDelaySchedule2.Delay)
}

Every 的实现:

// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
    if duration < time.Second {
        duration = time.Second
    }
    return ConstantDelaySchedule{
        Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
    }
}

6.3 Entry

定时任务对象:保存执行的任务Job、计算执行时间。

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
    // ID is the cron-assigned ID of this entry, which may be used to look up a
    // snapshot or remove it.
    ID EntryID
    // Schedule on which this job should be run.
      // 负责调度当前Entity中的Job执行
    Schedule Schedule
    // Next time the job will run, or the zero time if Cron has not been
    // started or this entry's schedule is unsatisfiable
      // Job下一次执行的时间
    Next time.Time
    // Prev is the last time this job was run, or the zero time if never.
      // 上一次执行时间
    Prev time.Time
    // WrappedJob is the thing to run when the Schedule is activated.
    WrappedJob Job
    // Job is the thing that was submitted to cron.
    // It is kept around so that user code that needs to get at the job later,
    // e.g. via Entries() can do so.
      // 要执行的Job
    Job Job
}

6.4 Cron

任务调度管理:保存定时任务对象(Entry),调度任务执行,提供新增、删除接口(涉及关联资源竞争)和暂停。

注意:

  • Cron 结构没有导出任何成员。
  • 有一个成员stop,类型是struct{},即空结构体。
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
    entries   []*Entry
    chain     Chain
    stop      chan struct{} // 控制Cron实例暂停
    add       chan *Entry    // 当Cron已经运行了,增加新的Entity是通过add这个channel实现的
    remove    chan EntryID
    snapshot  chan chan []Entry // 获取当前所有entity的快照
    running   bool // 当已经运行时为true,否则为false
    logger    Logger
    runningMu sync.Mutex
    location  *time.Location
    parser    Parser
    nextID    EntryID
    jobWaiter sync.WaitGroup
}
// Remove an entry from being run in the future.
func (c *Cron) Remove(id EntryID) {
    c.runningMu.Lock()
    defer c.runningMu.Unlock()
    if c.running {
        c.remove <- id
    } else {
        c.removeEntry(id)
    }
}
// Schedule adds a Job to the Cron to be run on the given schedule.
// The job is wrapped with the configured Chain.
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
    c.runningMu.Lock()
    defer c.runningMu.Unlock()
    c.nextID++
    entry := &Entry{
        ID:         c.nextID,
        Schedule:   schedule,
        WrappedJob: c.chain.Then(cmd),
        Job:        cmd,
    }
    if !c.running {
        c.entries = append(c.entries, entry)
    } else {
        c.add <- entry
    }
    return entry.ID
}

7、实例化主要方法说明

7.1 实例化

启动时会开启唯一协程执行run方法,计算任务执行时间,执行,任务管理等:

// New returns a new Cron job runner, modified by the given options.
//
// Available Settings
//
//   Time Zone
//     Description: The time zone in which schedules are interpreted
//     Default:     time.Local
//
//   Parser
//     Description: Parser converts cron spec strings into cron.Schedules.
//     Default:     Accepts this spec: https://en.wikipedia.org/wiki/Cron
//
//   Chain
//     Description: Wrap submitted jobs to customize behavior.
//     Default:     A chain that recovers panics and logs them to stderr.
//
// See "cron.With*" to modify the default behavior.
// 实例化时,成员使用的基本是默认值
func New(opts ...Option) *Cron {
    c := &Cron{
        entries:   nil,
        chain:     NewChain(),
        add:       make(chan *Entry),
        stop:      make(chan struct{}),
        snapshot:  make(chan chan []Entry),
        remove:    make(chan EntryID),
        running:   false,
        runningMu: sync.Mutex{},
        logger:    DefaultLogger,
        location:  time.Local,
        parser:    standardParser,
    }
    for _, opt := range opts {
        opt(c)
    }
    return c
}
// Start the cron scheduler in its own goroutine, or no-op if already started.
func (c *Cron) Start() {
    c.runningMu.Lock()
    defer c.runningMu.Unlock()
    if c.running {
        return
    }
    c.running = true
    go c.run()
}

7.2 主要方法

核心调度:计算下次执行时间 -> 排序 -> 取最早执行数据 -> timer 等待,因为只有一个协程在执行这个run的调

度,所以不存在资源竞争,不需要加锁,另外考虑到执行任务可能涉及阻塞,例如:IO操作,所以一般startJob方

法会开启协程执行。

// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {
    c.runningMu.Lock()
    if c.running {
        c.runningMu.Unlock()
        return
    }
    c.running = true
    c.runningMu.Unlock()
    c.run()
}
// run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
    c.logger.Info("start")
    // Figure out the next activation times for each entry.
    now := c.now()
    for _, entry := range c.entries {
        entry.Next = entry.Schedule.Next(now)
        c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
    }
    for {
        // Determine the next entry to run.
        sort.Sort(byTime(c.entries))
        var timer *time.Timer
        if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
            // If there are no entries yet, just sleep - it still handles new entries
            // and stop requests.
            timer = time.NewTimer(100000 * time.Hour)
        } else {
            timer = time.NewTimer(c.entries[0].Next.Sub(now))
        }
        for {
            select {
            case now = <-timer.C:
                now = now.In(c.location)
                c.logger.Info("wake", "now", now)
                // Run every entry whose next time was less than now
                for _, e := range c.entries {
                    if e.Next.After(now) || e.Next.IsZero() {
                        break
                    }
                    c.startJob(e.WrappedJob)
                    e.Prev = e.Next
                    e.Next = e.Schedule.Next(now)
                    c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
                }
            case newEntry := <-c.add:
                timer.Stop()
                now = c.now()
                newEntry.Next = newEntry.Schedule.Next(now)
                c.entries = append(c.entries, newEntry)
                c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
            case replyChan := <-c.snapshot:
                replyChan <- c.entrySnapshot()
                continue
            case <-c.stop:
                timer.Stop()
                c.logger.Info("stop")
                return
            case id := <-c.remove:
                timer.Stop()
                now = c.now()
                c.removeEntry(id)
                c.logger.Info("removed", "entry", id)
            }
            break
        }
    }
}
// startJob runs the given job in a new goroutine.
func (c *Cron) startJob(j Job) {
    c.jobWaiter.Add(1)
    go func() {
        defer c.jobWaiter.Done()
        j.Run()
    }()
}

7.3 其它成员方法

// EntryID标识Cron实例中的entry
type EntryID int
// 将job加入Cron中
// 如上所述,该方法只是简单的通过FuncJob类型强制转换cmd,然后调用AddJob方法
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) 
// 将job加入Cron中
// 通过Parse函数解析cron表达式spec的到调度器实例(Schedule),之后调用c.Schedule方法
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error)
// 获取当前Cron总所有Entities的快照
func (c *Cron) Entries() []*Entry
// Location获取时区位置
func (c *Cron) Location() *time.Location 
// Entry返回给定项的快照,如果找不到则返回nil
func (c *Cron) Entry(id EntryID) Entry
// 删除将来运行的条目
func (c *Cron) Remove(id EntryID) 
// 通过两个参数实例化一个Entity,然后加入当前Cron中
// 注意: 如果当前Cron未运行,则直接将该entity加入Cron中
// 否则,通过add这个成员channel将entity加入正在运行的Cron中
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID
// 新启动一个goroutine运行当前Cron
func (c *Cron) Start()
// 通过给stop成员发送一个struct{}{}来停止当前Cron,同时将running置为false
// 从这里知道,stop只是通知Cron停止,因此往channel发一个值即可,而不关心值是多少
// 所以,成员stop定义为空struct
func (c *Cron) Stop()
// 运行cron调度程序,如果已经在运行,则不运行op
func (c *Cron) Run()

8、其它定时任务的实现

8.1 最简单的定时任务

使用协程和 Sleep方式:

package main
import (
    "fmt"
    "time"
)
func main(){
    go func() {
        for true {
            fmt.Println("Hello World!",time.Now())
            time.Sleep(1 * time.Second)
        }
    }()
    select {
    }
}

# 输出
Hello World! 2023-05-30 22:26:12.454977 +0800 CST m=+7.021813601
Hello World! 2023-05-30 22:26:13.4553086 +0800 CST m=+8.022145201
Hello World! 2023-05-30 22:26:14.4555344 +0800 CST m=+9.022371001
Hello World! 2023-05-30 22:26:15.4566625 +0800 CST m=+10.023499101
Hello World! 2023-05-30 22:26:16.4570511 +0800 CST m=+11.023887701
Hello World! 2023-05-30 22:26:17.4579146 +0800 CST m=+12.024751201
Hello World! 2023-05-30 22:26:18.4589781 +0800 CST m=+13.025814701
......

8.2 Timer实现定时任务

除了使用 cron 库可以实现定时任务,使用 time 库也可以实现定时任务。

在Go语言中,可以使用 time 包提供的 Timer 和 Ticker 类型设置定时任务。Timer 用于在未来的某个时间点执行

一次任务,而 Ticker 则用于每隔一定时间执行一次任务。

8.2.1 Timer启动定时器

Timer 实现定时器,延迟执行,这个定时器只会触发一次。

下面是一个使用 Timer 设置定时任务的例子:

package main
import (
    "fmt"
    "time"
)
func main() {
    // 创建一个Timer实例,设置2秒后执行任务
    t := time.NewTimer(2 * time.Second)
    // 记得释放Timer资源
    defer t.Stop()
    // 等待Timer到期
    <-t.C
    // 执行任务
    fmt.Println("Task executed at", time.Now())
}

# 输出
Task executed at 2023-05-30 21:58:32.5390386 +0800 CST m=+2.002460501

8.2.2 Timer停止定时器

使用 time.Stop() 停止定时器,通过向通道发送一个信号,通知定时器是否关闭。

package main
import (
    "fmt"
    "time"
)
func main() {
    done := make(chan bool)
    ticker := time.NewTimer(1 * time.Second)
    go func() {
        for {
            select {
            case <-done:
                ticker.Stop()
                return
            case <-ticker.C:
                fmt.Println("Hello World!")
            }
        }
    }()
    time.Sleep(10 *time.Second)
    done <- true
}

# 输出
Hello World!

8.2.3 Timer重置定时器

package main
import (
    "fmt"
    "time"
)
func main() {
    fmt.Println("hello world", time.Now())
    // 创建一个定时器
    // 设置7秒后执行一次
    myT := time.NewTimer(7 * time.Second)
    // 重置定时器为1s后执行
    myT.Reset(1 * time.Second)
    <-myT.C
    fmt.Println("hello world", time.Now())
}

# 输出
hello world 2023-05-30 22:13:03.4342176 +0800 CST m=+0.002177501
hello world 2023-05-30 22:13:04.4454858 +0800 CST m=+1.013445701

8.2.4 Ticker启动定时器

Ticker 也是定时器,它是一个周期性的定时器。

package main
import (
    "fmt"
    "time"
)
func main() {
    // 创建一个Ticker实例,每隔1秒执行一次任务
    ticker := time.NewTicker(1 * time.Second)
    // 记得释放Ticker资源
    defer ticker.Stop()
    // 循环处理任务
    for {
        // 等待Ticker的下一次触发
        <-ticker.C
        // 执行任务
        fmt.Println("Task executed at", time.Now())
    }
}

# 输出
Task executed at 2023-05-30 22:17:57.2393424 +0800 CST m=+1.007115201
Task executed at 2023-05-30 22:17:58.2428548 +0800 CST m=+2.010627601
Task executed at 2023-05-30 22:17:59.2431966 +0800 CST m=+3.010969401
Task executed at 2023-05-30 22:18:00.2455851 +0800 CST m=+4.013357901
Task executed at 2023-05-30 22:18:01.2438882 +0800 CST m=+5.011661001
......

package main
import (
    "fmt"
    "time"
)
func main() {
    // 创建一个定时器,每隔1秒触发一次
    ticker := time.NewTicker(1 * time.Second)
    // 在函数退出时停止定时器
    defer ticker.Stop()
    // timer实现定时器(延迟执行),这个定时器只会触发一次,所以想要执行定时任务需要放在for循环中
    for {
        select {
        // 定时器触发时执行的任务
        case <-ticker.C:
            fmt.Println("hello world", time.Now())
        }
    }
}

# 输出
hello world 2023-05-30 21:15:42.32353 +0800 CST m=+42.004562601
hello world 2023-05-30 21:15:43.3274285 +0800 CST m=+43.008461101
hello world 2023-05-30 21:15:44.3226011 +0800 CST m=+44.003633701
hello world 2023-05-30 21:15:45.3233505 +0800 CST m=+45.004383101
hello world 2023-05-30 21:15:46.3310151 +0800 CST m=+46.012047701
hello world 2023-05-30 21:15:47.3234301 +0800 CST m=+47.004462701
hello world 2023-05-30 21:15:48.3243248 +0800 CST m=+48.005357401
......

8.2.5 Ticker停止定时器

package main
import (
    "fmt"
    "time"
)
func main() {
    ticker := time.NewTicker(1 * time.Second)
    go func() {
        for range ticker.C {
            fmt.Println("Hello World!")
        }
    }()
    time.Sleep(10 * time.Second)
    ticker.Stop()
}

# 输出
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

package main
import (
    "fmt"
    "time"
)
func main() {
    done := make(chan bool)
    ticker := time.NewTicker(1 * time.Second)
    go func() {
        for {
            select {
            case <-done:
                ticker.Stop()
                return
            case <-ticker.C:
                fmt.Println("Hello World!")
            }
        }
    }()
    time.Sleep(10 *time.Second)
    done <- true
}

# 输出
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

8.3 gocron库

8.3.1 安装

go get -u github.com/go-co-op/gocron

8.3.2 使用

s := gocron.NewScheduler(time.UTC)
s.Every(5).Seconds().Do(func(){ ... }) 
s.Every("5m").Do(func(){ ... })
s.Every(5).Days().Do(fu
s.Every(1).Month(1, 2, 3).Do(func(){ ... })
s.Every(1).Day().At("10:30").Do(func(){ ... })
s.Every(1).Day().At("10:30;08:00").Do(func(){ ... })
s.Every(1).Day().At("10:30").At("08:00").Do(func(){ ... })
s.Every(1).MonthLastDay().Do(func(){ ... })
s.Every(2).MonthLastDay().Do(func(){ ... })
s.Cron("*/1 * * * *").Do(task) 
s.StartAsync()
s.StartBlocking()

8.3.3 例子

package main
import (
    "fmt"
    "time"
    "github.com/go-co-op/gocron"
)
func cron1() {
    fmt.Println("cron1",time.Now())
}
func cron2() {
    fmt.Println("cron2",time.Now())
}
func main() {
    timezone, _ := time.LoadLocation("Asia/Shanghai")
    s := gocron.NewScheduler(timezone)
    // 每秒执行一次
    s.Every(1).Seconds().Do(func() {
        go cron1()
    })
    // 每秒执行一次
    s.Every(1).Second().Do(func() {
        go cron2()
    })
    s.StartBlocking()
}

# 输出
cron2 2023-05-30 22:28:09.1476998 +0800 CST m=+0.002582801
cron1 2023-05-30 22:28:09.1476998 +0800 CST m=+0.002582801
cron2 2023-05-30 22:28:10.1478397 +0800 CST m=+1.002722701
cron1 2023-05-30 22:28:10.1478397 +0800 CST m=+1.002722701
cron1 2023-05-30 22:28:11.1487562 +0800 CST m=+2.003639201
cron2 2023-05-30 22:28:11.1487562 +0800 CST m=+2.003639201
cron2 2023-05-30 22:28:12.1479533 +0800 CST m=+3.002836301
cron1 2023-05-30 22:28:12.1479533 +0800 CST m=+3.002836301
......

到此这篇关于Go语言定时任务的实现示例的文章就介绍到这了,更多相关Go语言定时任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • Golang map实践及实现原理解析

    Golang map实践及实现原理解析

    这篇文章主要介绍了Golang map实践以及实现原理,Go 语言中,通过哈希查找表实现 map,用链表法解决哈希冲突,本文结合实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2022-06-06
  • Golang中使用errors返回调用堆栈信息

    Golang中使用errors返回调用堆栈信息

    这篇文章给大家介绍了Golang中如何使用errors返回调用堆栈信息,文章通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-12-12
  • golang解析html网页的方法

    golang解析html网页的方法

    今天小编就为大家分享一篇golang解析html网页的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • 详解go中的defer链如何被遍历执行

    详解go中的defer链如何被遍历执行

    为了在退出函数前执行一些资源清理的操作,例如关闭文件、释放连接、释放锁资源等,会在函数里写上多个defer语句,多个_defer 结构体形成一个链表,G 结构体中某个字段指向此链表,那么go中的defer链如何被遍历执行,本文将给大家详细的介绍,感兴趣的朋友可以参考下
    2024-01-01
  • golang中实现graphql请求的方法

    golang中实现graphql请求的方法

    这篇文章主要介绍了如何在golang中实现graphql请求,在本文中,我们介绍了如何使用gqlgen来构建GraphQL服务,需要的朋友可以参考下
    2023-04-04
  • golang配置文件解析器之goconfig框架的使用详解

    golang配置文件解析器之goconfig框架的使用详解

    goconfig是一个易于使用,支持注释的 Go 语言配置文件解析器,该文件的书写格式和 Windows 下的 INI 文件一样,本文主要为大家介绍了goconfig框架的具体使用,需要的可以参考下
    2023-11-11
  • 深入解析Go 变量字符串与字符编码问题

    深入解析Go 变量字符串与字符编码问题

    Go的字符串是由单个字节连接起来的,Go语言的字符串的字节使用UTF-8编码标识Unicode文本,接下来通过本文给大家介绍下Go变量字符串与字符编码问题,需要的朋友可以参考下
    2022-04-04
  • go语言按显示长度截取字符串的方法

    go语言按显示长度截取字符串的方法

    这篇文章主要介绍了go语言按显示长度截取字符串的方法,涉及Go语言操作字符串的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • Go语言学习教程之反射的示例详解

    Go语言学习教程之反射的示例详解

    这篇文章主要通过记录对reflect包的简单使用,来对反射有一定的了解。文中的示例代码讲解详细,对我们学习Go语言有一定帮助,需要的可以参考一下
    2022-09-09
  • GoLang 中的随机数的示例代码

    GoLang 中的随机数的示例代码

    本篇文章主要介绍了GoLang 中的随机数的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03

最新评论